home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 August: Tool Chest / Dev.CD Aug 98 TC.toast / Sample Code / Text / SimpleText Sample / SimpleText.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-10-17  |  144.5 KB  |  5,494 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        SimpleText.c
  3.  
  4.     Contains:    SimpleText - a simple document editing application for shipping
  5.                              with system software.
  6.  
  7.     Version:    SimpleText 1.4 or later
  8.  
  9. ** Copyright 1993-1996 Apple Computer. All rights reserved.
  10. **
  11. **    You may incorporate this sample code into your applications without
  12. **    restriction, though the sample code has been provided "AS IS" and the
  13. **    responsibility for its operation is 100% yours.  However, what you are
  14. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  15. **    after having made changes. If you're going to re-distribute the source,
  16. **    we require that you make it clear in the source that the code was
  17. **    descended from Apple Sample Code, but that you've made changes.
  18.  
  19. */
  20.  
  21. #include "MacIncludes.h"
  22.  
  23. #include <ImageCompression.h>    // for CustomGetFilePreview
  24. #include <Threads.h>
  25.  
  26. #define CompilingMain=1
  27. #include "SimpleText.h"
  28. #include "Clipboard.h"
  29.  
  30. // --------------------------------------------------------------------------------------------------------------
  31. // INTERNAL TYPES AND TYPEDEFS
  32. // --------------------------------------------------------------------------------------------------------------
  33.  
  34.  
  35. // refCon value between SimpleCatchShape and GXInstallQDTranslator    
  36. typedef struct
  37.     {
  38.     gxShape         thePage;
  39.     gxRectangle        thePageRectangle;
  40.     Boolean            doLayout;
  41.     gxJob            theJob;
  42.     } CatchRefCon;
  43.  
  44. // --------------------------------------------------------------------------------------------------------------
  45. // FORWARD DECLARES
  46. // --------------------------------------------------------------------------------------------------------------
  47. OSErr     DoActivate(WindowRef pWindow, Boolean activating);
  48. OSErr    DoCommand(WindowRef pWindow, short commandID, long menuResult);
  49. OSErr    DoKeyEvent(WindowRef pWindow, EventRecord * pEvent, Boolean processPageControls);
  50. Boolean CommandToIDs(short commandID, short * menuID, short *itemID);
  51. void     AdjustMenus(WindowRef pWindow, Boolean editDialogs, Boolean forceTitlesOn);
  52.  
  53. // --------------------------------------------------------------------------------------------------------------
  54. // GLOBAL VARIABLES
  55. // --------------------------------------------------------------------------------------------------------------
  56. EventRecord            gEvent;                    // currently pending event
  57. Boolean                gAllDone;                // true if the application is the in process of terminating
  58. MachineInfoRec        gMachineInfo;            // info about abilities and options installed on this machine
  59. short                gApplicationResFile;    // resource fork of application
  60. RgnHandle            gCursorRgn;                // region to control the cursor apearence
  61. AGRefNum            gAGRefNum = -1;            // AppleGuide database which is open
  62. FSSpec                gAGSpec;                // where to find our database
  63. AGCoachRefNum        gAGCoachRefNum = -1;    // coach handler refNum
  64. FontMappingHandle    gFontMappingList = nil;    // list of font mappings
  65. ThreadID            gFontThread;            // thread that builds font menu
  66. ThreadID            gAGThread;                // thread that looks for AppleGuide database
  67. ThreadID            gStarterThread;            // starts our other threads for us
  68. Boolean                gDontYield;                // whether our threads should yield
  69. void*                gThreadResults;            // scratch space for thread results
  70.  
  71. // These variables are for the find/replace commands
  72. Str255            gFindString = "\p", gReplaceString = "\p";
  73. Boolean            gWrapAround = false, gCaseSensitive = false;
  74.  
  75. // Metrowerks MWCRuntime.lib defines qd for us on PPC, and their
  76. // __runtime module does under the 68K case. OTOH, neither SC nor
  77. // MrC give us qd for free, so we need it there. I'm still not
  78. // certain which way to go for the ThinkC or Symantec PPC case.
  79. #if !defined(__MWERKS__)
  80. // QuickDraw globals
  81. QDGlobals        qd;
  82. #endif
  83.  
  84. // --------------------------------------------------------------------------------------------------------------
  85. #pragma segment Utility
  86.  
  87. static pascal Boolean AlertFilter(DialogRef theDialog, EventRecord *theEvent, short *itemHit)
  88. {
  89.     if (theEvent->what == activateEvt && (DialogRef) theEvent->message == theDialog)
  90.         {
  91.         SetDialogDefaultItem(theDialog, 1);
  92.         }
  93.  
  94.     if (StdFilterProc(theDialog, theEvent, itemHit))
  95.         return true;
  96.  
  97.     // Pass updates through (Activates are tricky...was mucking with Apple menu & thereby
  98.     // drastically changing how the system handles the menu bar during our alert)
  99.     if (theEvent->what == updateEvt /* || theEvent->what == activateEvt */ )
  100.         {
  101.         HandleEvent(theEvent);
  102.         }
  103.  
  104.     return false;
  105.  
  106. } // AlertFilter
  107.  
  108.  
  109. void ConductErrorDialog(OSErr error, short commandID, short alertType)
  110. {
  111.     long        foundError;            // The error, converted to a number
  112.     short        stringIndex;        // Index into the strings
  113.     Str255        errorText;            // the error in a string format
  114.     
  115.     // Start with no error so far
  116.     foundError = 0;
  117.     
  118.     // Start with the first string
  119.     stringIndex = 1;
  120.     
  121.     // Loop until we find an error string
  122.     errorText[0] = 0;
  123.  
  124.     do
  125.         {
  126.         // Get the string, and convert it to a number
  127.         GetIndString(errorText, kErrorBaseID + commandID, stringIndex);
  128.         if (errorText[0] == 0)
  129.             break;
  130.         StringToNum(errorText, &foundError);
  131.         
  132.         // If we reach the last string, or we match the error code
  133.         if ((foundError == 0) ||
  134.             (foundError == error))
  135.             {
  136.             // Get the text string for this error
  137.             GetIndString(errorText, kErrorBaseID + commandID, stringIndex+1);
  138.             }
  139.         else
  140.             {
  141.             // Otherwise, make us continue until we get a string
  142.             errorText[0] = 0;
  143.             }
  144.             
  145.         // Advance so we get the next string number
  146.         stringIndex += 2;
  147.         
  148.         } while (errorText[0] == 0);                // errorText[0] == 0
  149.         
  150.     if (errorText[0] != 0)
  151.         {
  152.         DialogRef    dPtr;
  153.         short        hit;
  154.         
  155.         SetCursor(&qd.arrow);
  156.         ParamText(errorText, "\p", "\p", "\p");
  157.         
  158.         #if !GENERATINGPOWERPC
  159.             if (gMachineInfo.theEnvirons.systemVersion < 0x0700)
  160.                 {
  161.                 short ** hDialog;
  162.                 
  163.                 hDialog = (short**) GetResource('DLOG', kErrorBaseID + alertType);
  164.                 (*hDialog)[4] = dBoxProc;
  165.                 
  166.                 dPtr = GetNewDialog(kErrorBaseID + alertType, nil, (WindowRef)-1);
  167.                 
  168.                 do
  169.                     {
  170.                     ModalDialog(nil, &hit);
  171.                     } while (hit != ok);
  172.                 
  173.                 DisposeDialog(dPtr);
  174.                 }
  175.             else
  176.                 {
  177.                 dPtr = GetNewDialog(kErrorBaseID + alertType, nil, (WindowRef)-1);
  178.                 
  179.                 SetDialogDefaultItem(dPtr, ok);
  180.                 
  181.                 BeginMovableModal();
  182.                 
  183.                 do
  184.                     {
  185.                     MovableModalDialog(nil, &hit);
  186.                     } while (hit != ok);
  187.                 
  188.                 DisposeDialog(dPtr);
  189.                 EndMovableModal();
  190.                 }
  191.         #else
  192.             dPtr = GetNewDialog(kErrorBaseID + alertType, nil, (WindowRef)-1);
  193.             
  194.             SetDialogDefaultItem(dPtr, ok);
  195.             
  196.             BeginMovableModal();
  197.             
  198.             do
  199.                 {
  200.                 MovableModalDialog(nil, &hit);
  201.                 } while (hit != ok);
  202.             
  203.             DisposeDialog(dPtr);
  204.             EndMovableModal();
  205.         #endif
  206.         }
  207.         
  208. } // ConductErrorDialog
  209.  
  210. // --------------------------------------------------------------------------------------------------------------
  211. #pragma segment Utility
  212.  
  213. static void MovableModalMenus(DialogRef dPtr, short *pItem, long menuResult)
  214. {
  215.     short    iCut, iCopy, iClear, iPaste;
  216.     short    editMenu;
  217.     short    menuItem = menuResult & 0xFFFF;
  218.     
  219.     // find out where edit menus are
  220.     CommandToIDs(cCut, &editMenu, &iCut);
  221.     CommandToIDs(cCopy, &editMenu, &iCopy);
  222.     CommandToIDs(cClear, &editMenu, &iClear);
  223.     CommandToIDs(cPaste, &editMenu, &iPaste);
  224.     
  225.     HiliteMenu(0);
  226.     switch (menuResult >> 16)
  227.         {
  228.         case mApple:
  229.             {
  230.             Str255    tempString;
  231.             
  232.             GetMenuItemText(GetMenuHandle(menuResult>>16), menuItem, tempString);
  233.             OpenDeskAcc(tempString);
  234.             }
  235.             break;
  236.             
  237.         case mEdit:
  238.             {
  239.             short    type;
  240.             Handle    item;
  241.             Rect    box;
  242.             short    editField = GetDialogKeyboardFocusItem(dPtr);
  243.             
  244.             // return typed item, if it isn't disabled
  245.             GetDialogItem(dPtr, editField, &type, &item, &box);
  246.             if ((type & itemDisable) == 0)
  247.                 *pItem = editField;
  248.                 
  249.             if (menuItem == iCut)
  250.                 {
  251.                 DialogCut(dPtr);
  252.                 ZeroScrap();
  253.                 TEToScrap();
  254.                 }
  255.                 
  256.             if (menuItem == iCopy)
  257.                 {
  258.                 DialogCopy(dPtr);
  259.                 ZeroScrap();
  260.                 TEToScrap();
  261.                 }
  262.                 
  263.             if (menuItem == iClear)
  264.                 DialogDelete(dPtr);
  265.                 
  266.             if (menuItem == iPaste)
  267.                 DialogPaste(dPtr);
  268.             }
  269.             break;
  270.         }
  271.         
  272. } // MovableModalMenus
  273.  
  274. // --------------------------------------------------------------------------------------------------------------
  275. #pragma segment Utility
  276.  
  277. void MovableModalDialog(ModalFilterProcPtr filterProc, short *pItem)
  278. /*
  279.     Call this as you would ModalDialog, when the dialog is moveable
  280.     modal.
  281.     
  282.     However, first call BeginMovableModal, and afterwards (after
  283.     disposing of dialog) call EndMovableModal.
  284. */
  285. {
  286.     GrafPtr     curPort;
  287.     DialogRef    dPtr = FrontWindow();
  288.     
  289.     *pItem = 0;    
  290.     if (dPtr)
  291.         {
  292.         GetPort(&curPort);
  293.         SetPort(dPtr);
  294.         
  295.         do
  296.             {
  297.             WaitNextEvent(mDownMask + mUpMask + keyDownMask + keyUpMask + autoKeyMask + updateMask + activMask + osMask,
  298.                             &gEvent, 0, nil);
  299.             
  300.             // call the filter proc
  301.             if ( (filterProc) && ((*filterProc) (dPtr, &gEvent, pItem)) )
  302.                 break;
  303.                             
  304.             // call the basic filtering
  305.             if (StdFilterProc(dPtr, &gEvent, pItem))
  306.                 break;
  307.                 
  308.             // handle keyboard
  309.             if ((gEvent.what == keyDown) && (gEvent.modifiers & cmdKey))
  310.                 {
  311.                 MovableModalMenus(dPtr, pItem, MenuKey(gEvent.message & charCodeMask));
  312.                 break;
  313.                 }
  314.                 
  315.             // handle clicks and drags
  316.             if (gEvent.what == mouseDown)
  317.                 {
  318.                 WindowRef    whichWindow;
  319.                 short        part = FindWindow(gEvent.where, &whichWindow);
  320.                 
  321.                 // menu bar events
  322.                 if (part == inMenuBar)
  323.                     {
  324.                     MovableModalMenus(dPtr, pItem, MenuSelect(gEvent.where));
  325.                     break;
  326.                     }
  327.                     
  328.                 // check for outside of our window
  329.                 if (!PtInRgn(gEvent.where, ((WindowPeek)dPtr)->strucRgn))
  330.                     {
  331.                     SysBeep(1);
  332.                     gEvent.what = nullEvent;
  333.                     }
  334.                     
  335.                 // drag the window around
  336.                 if ( (part == inDrag) && (whichWindow == dPtr) )
  337.                     {
  338.                     Rect    tempRect = (**GetGrayRgn()).rgnBBox;
  339.                     
  340.                     DragWindow(GetDialogWindow(dPtr), gEvent.where, &tempRect);
  341.                     gEvent.what = nullEvent;
  342.                     }
  343.                 }
  344.                 
  345.             // check with standard dialog stuff    
  346.             {
  347.             DialogRef    tempDialog;
  348.             
  349.             if ( IsDialogEvent(&gEvent) && DialogSelect(&gEvent, &tempDialog, pItem) )
  350.                 break;
  351.             }
  352.             
  353.             // handle updates
  354.             if (gEvent.what == updateEvt)
  355.                 {
  356.                 HandleEvent(&gEvent);
  357.                 break;
  358.                 }
  359.             } while (true);
  360.         
  361.         SetPort(curPort);
  362.         }
  363.         
  364. } // MovableModalDialog
  365.  
  366. // --------------------------------------------------------------------------------------------------------------
  367. #pragma segment Utility
  368.  
  369. void BeginMovableModal(void)
  370. {
  371.     DialogRef    dPtr = FrontWindow();
  372.     WindowRef    nextWindow = GetNextWindow(dPtr);
  373.     
  374.     if (nextWindow)
  375.         DoActivate(nextWindow, false);
  376.     AdjustMenus(GetDialogWindow(dPtr), (GetDialogKeyboardFocusItem(dPtr) > 0), false);
  377.  
  378. } // BeginMovableModal
  379.  
  380. // --------------------------------------------------------------------------------------------------------------
  381. #pragma segment Utility
  382.  
  383. void EndMovableModal(void)
  384. {
  385.     WindowRef    nextWindow = FrontWindow();
  386.     
  387.     AdjustMenus(nextWindow, true, false);
  388.     if (nextWindow)
  389.         DoActivate(nextWindow, true);
  390.     
  391. } // EndMovableModal
  392.  
  393. // --------------------------------------------------------------------------------------------------------------
  394. #pragma segment Utility
  395.  
  396. short ConductFindOrReplaceDialog(short dialogID)
  397. {
  398.     DialogRef    dPtr;
  399.     short        hit;
  400.     
  401.     dPtr = GetNewDialog(dialogID, nil, (WindowRef)-1);
  402.     if (dPtr)
  403.         {
  404.         short    kind;
  405.         Rect    box;
  406.         Handle    item;
  407.         
  408.         // standard default behavior
  409.         SetDialogDefaultItem(dPtr, ok);
  410.         SetDialogCancelItem (dPtr, cancel);
  411.         SetDialogTracksCursor(dPtr, true);
  412.         
  413.         // Find string
  414.         GetDialogItem(dPtr, iFindEdit, &kind, &item, &box);
  415.         SetDialogItemText(item, gFindString);
  416.  
  417.         // check boxes
  418.         GetDialogItem(dPtr, iCaseSensitive, &kind, &item, &box);
  419.         SetControlValue((ControlRef)item, gCaseSensitive);
  420.         GetDialogItem(dPtr, iWrapAround, &kind, &item, &box);
  421.         SetControlValue((ControlRef)item, gWrapAround);
  422.         
  423.         if (dialogID == kReplaceWindowID)
  424.             {
  425.             // Replace string
  426.             GetDialogItem(dPtr, iReplaceEdit, &kind, &item, &box);
  427.             SetDialogItemText(item, gReplaceString);
  428.             }
  429.         
  430.         // select the search text by default
  431.         SelectDialogItemText(dPtr, iFindEdit, 0, 32767);
  432.         
  433.         // and away we go!
  434.         ShowWindow(GetDialogWindow(dPtr));
  435.         BeginMovableModal();
  436.         
  437.         do
  438.             {
  439.             MovableModalDialog(nil, &hit);
  440.             switch (hit)
  441.                 {
  442.                 case iCaseSensitive:
  443.                 case iWrapAround:
  444.                     GetDialogItem(dPtr, hit, &kind, &item, &box);
  445.                     SetControlValue((ControlRef)item, 1-GetControlValue((ControlRef)item));
  446.                     break;
  447.                 }
  448.             } while ( (hit != ok) && (hit != cancel) && (hit != iReplaceAll) );
  449.         
  450.         if (hit != cancel)
  451.             {
  452.             // Find string
  453.             GetDialogItem(dPtr, iFindEdit, &kind, &item, &box);
  454.             GetDialogItemText(item, gFindString);
  455.     
  456.             // check boxes
  457.             GetDialogItem(dPtr, iCaseSensitive, &kind, &item, &box);
  458.             gCaseSensitive = GetControlValue((ControlRef)item);
  459.             GetDialogItem(dPtr, iWrapAround, &kind, &item, &box);
  460.             gWrapAround = GetControlValue((ControlRef)item);
  461.             
  462.             if (dialogID == kReplaceWindowID)
  463.                 {
  464.                 // Replace string
  465.                 GetDialogItem(dPtr, iReplaceEdit, &kind, &item, &box);
  466.                 GetDialogItemText(item, gReplaceString);
  467.                 }
  468.             }
  469.             
  470.         DisposeDialog(dPtr);
  471.         EndMovableModal();
  472.         }
  473.         
  474.     return(hit);
  475.     
  476. } // ConductFindOrReplaceDialog
  477.  
  478. // --------------------------------------------------------------------------------------------------------------
  479. #pragma segment Utility
  480.  
  481. void SetWatchCursor(void)
  482. {
  483.     CursHandle    theWatch;
  484.         
  485.     theWatch = GetCursor(watchCursor);
  486.     if (theWatch)
  487.         {
  488.         char    oldState;
  489.         
  490.         oldState = HGetState((Handle) theWatch);
  491.         HLock((Handle) theWatch);
  492.         SetCursor(*theWatch);
  493.         HSetState((Handle) theWatch, oldState);
  494.         }
  495.         
  496. } // SetWatchCursor
  497.  
  498. // --------------------------------------------------------------------------------------------------------------
  499. #pragma segment Utility
  500.  
  501. void LongRectToRect(LongRect* longRect, Rect *rect)
  502. {
  503.     rect->top         = longRect->top;
  504.     rect->left         = longRect->left;
  505.     rect->bottom     = longRect->bottom;
  506.     rect->right     = longRect->right;
  507.     
  508. } // LongRectToRect
  509.  
  510. // --------------------------------------------------------------------------------------------------------------
  511. #pragma segment Utility
  512.  
  513. void RectToLongRect(Rect *rect, LongRect *longRect)
  514. {
  515.     longRect->top         = rect->top;
  516.     longRect->left         = rect->left;
  517.     longRect->bottom     = rect->bottom;
  518.     longRect->right     = rect->right;
  519.     
  520. } // RectToLongRect
  521.  
  522. // --------------------------------------------------------------------------------------------------------------
  523. #pragma segment Utility
  524.  
  525. void GetPICTRectangleAt72dpi(PicHandle hPicture, Rect *pictureRect)
  526. {
  527.     typedef struct FixedRect {
  528.         Fixed left;
  529.         Fixed top;
  530.         Fixed right;
  531.         Fixed bottom;
  532.     } FixedRect;
  533.     
  534.     typedef struct {
  535.         Picture                pictInfo;
  536.         unsigned short        versionOp;        // 0x1101
  537.         Byte                opCodes[1];
  538.     } PICTHeaderVer1;
  539.     
  540.     typedef struct {
  541.         Picture            pictInfo;
  542.         unsigned short    versionOp;        // 0x0011
  543.         unsigned short    versionOp2;        // 0x02ff
  544.         unsigned short    headerOp;        // 0x0c00
  545.         unsigned short    version;        // 0xffff
  546.         unsigned short    version2;        // 0xffff
  547.         FixedRect        pictBounds;
  548.         unsigned long    reserved;
  549.         unsigned short    opCodes[1];
  550.     } PICTHeaderVer2;
  551.     
  552.     typedef struct {
  553.         Picture            pictInfo;
  554.         unsigned short    versionOp;        // 0x0011
  555.         unsigned short    versionOp2;        // 0x02ff
  556.         unsigned short    headerOp;        // 0x0c00
  557.         unsigned short    version;        // 0xfffe
  558.         unsigned short    reserved;        // 0x0000
  559.         Fixed            hRes;
  560.         Fixed            vRes;
  561.         Rect            pictBounds;
  562.         unsigned long    reserved2;
  563.         unsigned short    opCodes[1];
  564.     } PICTHeaderVer2Ext;
  565.  
  566.     Fixed            hRes, vRes;
  567.     PICTHeaderVer1* pPict = (PICTHeaderVer1*) *hPicture;
  568.  
  569.     hRes = vRes = ff(72);        // assume 72 dpi
  570.  
  571.     if (pPict->versionOp == 0x0011) 
  572.         {    
  573.         // Version 2 PICT
  574.     
  575.         PICTHeaderVer2* pPict2 = (PICTHeaderVer2*) pPict;
  576.         
  577.         if (pPict2->version == 0xfffe) 
  578.             {    
  579.             // Extended Version 2
  580.             PICTHeaderVer2Ext* pPict2ext = (PICTHeaderVer2Ext*) pPict;
  581.             hRes = pPict2ext->hRes;
  582.             vRes = pPict2ext->vRes;
  583.             }
  584.         }
  585.  
  586.     hRes = FixDiv(hRes, ff(72));
  587.     vRes = FixDiv(vRes, ff(72));
  588.     pictureRect->left     = Fix2Long(FixDiv( ff((**hPicture).picFrame.left),         hRes ));
  589.     pictureRect->right     = Fix2Long(FixDiv( ff((**hPicture).picFrame.right),     hRes ));
  590.     pictureRect->top     = Fix2Long(FixDiv( ff((**hPicture).picFrame.top),         vRes ));
  591.     pictureRect->bottom = Fix2Long(FixDiv( ff((**hPicture).picFrame.bottom),     vRes ));
  592.     
  593. } // GetPICTRectangleAt72dpi
  594.  
  595. // --------------------------------------------------------------------------------------------------------------
  596. #pragma segment Utility
  597.  
  598. static WindowDataPtr    GetWindowInfo(WindowRef pWindow)
  599. {
  600.     WindowDataPtr result = nil;
  601.     
  602.     if     (
  603.         (pWindow) &&
  604.         (GetWindowKind(pWindow) == userKind)
  605.         )
  606.         result = (WindowDataPtr) GetWRefCon(pWindow);
  607.  
  608.     return result;
  609.     
  610. } // GetWindowInfo
  611.  
  612. // --------------------------------------------------------------------------------------------------------------
  613. #pragma segment Utility
  614.  
  615. static short ZeroStringSub(Str255 destString, Str255 subStr)
  616.     // returns number of substitutions performed
  617. {
  618.     OSErr    anErr;
  619.     Handle    destHandle = nil;
  620.     Handle    subHandle = nil;
  621.     short    count = 0;
  622.  
  623.     anErr = PtrToHand(&destString[1], &destHandle, destString[0]);
  624.     if (anErr == noErr)
  625.         {        
  626.         anErr = PtrToHand(&subStr[1], &subHandle, subStr[0]);
  627.         if (anErr == noErr)
  628.             {
  629.             count = ReplaceText(destHandle, subHandle, "\p^0");        // error or # of substitutions
  630.                         
  631.             destString[0] = GetHandleSize(destHandle);
  632.             BlockMoveData(*destHandle, &destString[1], destString[0]);
  633.             }
  634.         }
  635.  
  636.     DisposeHandle(destHandle);
  637.     DisposeHandle(subHandle);
  638.  
  639.     if (count < 0)
  640.         count = 0;        // change error code into count = 0 substitutions
  641.  
  642.     return count;
  643.  
  644. } // ZeroStringSub
  645.  
  646. // --------------------------------------------------------------------------------------------------------------
  647. // SEARCH/REPLACE UTILITY FUNCTIONS
  648. // --------------------------------------------------------------------------------------------------------------
  649. static Boolean IsThisTheString(
  650.             Ptr p,                        // pointer to check
  651.             Str255 searchString,        // string to check for
  652.             Boolean isCaseSensitive)    // case sensitive check or not
  653. /*
  654.     Returns true if the supplied string is at the specified offset.
  655.     Otherwise returns false.
  656. */
  657. {
  658.     Boolean    returnValue = false;
  659.     
  660.     if (isCaseSensitive)
  661.         returnValue = ( IUMagString(p, &searchString[1], searchString[0], searchString[0]) == 0 );
  662.     else
  663.         returnValue = ( IUMagIDString(p, &searchString[1], searchString[0], searchString[0]) == 0 );
  664.         
  665.     return(returnValue);
  666.     
  667. } // IsThisTheString
  668.  
  669. // --------------------------------------------------------------------------------------------------------------
  670.  
  671. Boolean PerformSearch(
  672.         Handle    h,                    // handle to search
  673.         long start,                    // offset to begin with
  674.         Str255 searchString,        // string to search for
  675.         Boolean isCaseSensitive,    // case sensitive search
  676.         Boolean isBackwards,        // search backwards from starting point
  677.         Boolean isWraparound,        // wrap search around from end->begining
  678.         long * pNewStart,            // returned new selection start
  679.         long * pNewEnd)                // returned new selection end
  680. /*
  681.     Performs a search on the supplied handle, starting at the provided
  682.     offset.  Returns the new selection start and end values, and true
  683.     if the search is successful.  Otherwise it returns false.
  684. */
  685. {
  686.     char    flags;
  687.     Ptr        startPtr;
  688.     Ptr        endPtr;
  689.     Ptr        searchPtr;
  690.     Boolean    foundIt = false;
  691.     
  692.     flags = HGetState(h);
  693.     HLock(h);
  694.             
  695.     // back up one when searching backwards, or we'll hit every time on the current
  696.     // character
  697.     if (isBackwards)
  698.         {
  699.         if (start != 0)
  700.             {
  701.             --start;
  702.             }
  703.         else
  704.             {
  705.             if (isWraparound)
  706.                 start = GetHandleSize(h);
  707.             else
  708.                 return(false);
  709.             }
  710.         }
  711.         
  712.     // determine the bounds of the searching
  713.     startPtr = (*h) + start;
  714.     if ( isWraparound )
  715.         {
  716.         if (isBackwards)
  717.             {
  718.             // go backwards until just after the start, or begining of
  719.             // document is start is the end
  720.             if (start == GetHandleSize(h))
  721.                 endPtr = *h;
  722.             else
  723.                 endPtr = startPtr + 1;
  724.             }
  725.         else
  726.             {
  727.             // go forwards until just before the start, or to the end
  728.             // of the document is the start is already the begining
  729.             if (start == 0)
  730.                 endPtr = *h + GetHandleSize(h);
  731.             else
  732.                 endPtr = startPtr - 1;
  733.             }
  734.         }
  735.     else
  736.         {
  737.         if (isBackwards)
  738.             {
  739.             // go back until hit begining of document
  740.             endPtr = *h-1;    
  741.             }
  742.         else
  743.             {
  744.             // go forward until hit end of document
  745.             endPtr = *h + GetHandleSize(h);
  746.             }
  747.         }
  748.         
  749.     searchPtr = startPtr;
  750.     while (searchPtr != endPtr)
  751.         {
  752.         if (IsThisTheString(searchPtr, searchString, isCaseSensitive))
  753.             {
  754.             foundIt = true;
  755.             *pNewStart = searchPtr - *h;
  756.             *pNewEnd = *pNewStart + searchString[0];
  757.             break;
  758.             }
  759.             
  760.         if (isBackwards)
  761.             --searchPtr;
  762.         else
  763.             ++searchPtr;
  764.             
  765.         if (isWraparound)
  766.             {
  767.             if (searchPtr < *h)
  768.                 searchPtr = *h + GetHandleSize(h);
  769.             if (searchPtr > *h + GetHandleSize(h))
  770.                 searchPtr = *h;
  771.             }
  772.         }
  773.         
  774.     HSetState(h, flags);
  775.     
  776.     return(foundIt);
  777.     
  778. } // PerformSearch
  779.  
  780. // --------------------------------------------------------------------------------------------------------------
  781. // SELECTION UTILITY ROUTINES
  782. // --------------------------------------------------------------------------------------------------------------
  783. void DrawSelection(WindowDataPtr pData, Rect *pSelection, short * pPhase, Boolean bumpPhase)
  784. {
  785.     if    (!EmptyRect(pSelection) ) 
  786.         {
  787.         RgnHandle    oldClip = NewRgn();
  788.         Pattern        aPattern;
  789.         Rect        newClip;
  790.  
  791.         
  792.         if     ( 
  793.             (bumpPhase) && 
  794.             (MOVESELECTION(TickCount()) ) 
  795.             )
  796.             {
  797.             if ((++(*pPhase)) > 7 )
  798.                 *pPhase = 1;
  799.             }
  800.             
  801.         // setup for drawing in this window
  802.         SetPort((GrafPtr) pData);
  803.         GetClip(oldClip);
  804.         PenMode(notPatXor);
  805.         
  806.         // offset the draw area (SetOrigin a must to preserve pattern appearence)
  807.         // and the clip area to avoid stepping on the scroll bars
  808.         SetOrigin(GetControlValue(pData->hScroll), GetControlValue(pData->vScroll));
  809.         newClip = pData->contentRect;
  810.         OffsetRect(&newClip, GetControlValue(pData->hScroll), GetControlValue(pData->vScroll));
  811.         ClipRect(&newClip);
  812.         
  813.         // do the draw
  814.         GetIndPattern(&aPattern, kPatternListID, (*pPhase)+1);
  815.         PenPat(&aPattern);
  816.         FrameRect(pSelection);
  817.         SetOrigin(0, 0);
  818.         
  819.         // restore the old port settings
  820.         SetClip(oldClip);
  821.         DisposeRgn(oldClip);
  822.         PenNormal();
  823.  
  824.         }
  825.  
  826. } // DrawSelection
  827.  
  828. // --------------------------------------------------------------------------------------------------------------
  829. OSErr SelectContents(WindowRef pWindow, WindowDataPtr pData, EventRecord *pEvent, Rect *pSelection, Rect *pContent, short *pPhase)
  830. {
  831.  
  832.     OSErr            anErr = noErr;
  833.     Point            clickPoint = pEvent->where;
  834.     Point            currentPoint;
  835.     Boolean         didJustScroll;
  836.     ControlRef        theControl;
  837.     
  838.     GlobalToLocal(&clickPoint);
  839.     if (FindControl(clickPoint, pWindow, &theControl) == 0)
  840.         {
  841.     
  842.         // move the click point into the proper range
  843.         clickPoint.h += GetControlValue(pData->hScroll);
  844.         clickPoint.v += GetControlValue(pData->vScroll);
  845.         
  846.         // if the shift key is held down then the selection starts from
  847.         // a preexisting point such that we are doing an expand/contract
  848.         // of the original selection
  849.         if (pEvent->modifiers & shiftKey)
  850.             {
  851.             if (clickPoint.h < pSelection->right)
  852.                 clickPoint.h = pSelection->right;
  853.             else
  854.                 clickPoint.h = pSelection->left;
  855.  
  856.             if (clickPoint.v < pSelection->bottom)
  857.                 clickPoint.v = pSelection->bottom;
  858.             else
  859.                 clickPoint.v = pSelection->top;
  860.             }
  861.                         
  862.         while (StillDown())
  863.             {                    
  864.             // get the current mouse 
  865.             GetMouse(¤tPoint);
  866.             
  867.             didJustScroll = false;
  868.             // scroll contents if needed
  869.             {
  870.             short    deltaH = 0;
  871.             short    deltaV = 0;
  872.             
  873.             if (currentPoint.h < 0)
  874.                 deltaH = pData->hScrollAmount;
  875.             if (currentPoint.h > qd.thePort->portRect.right)
  876.                 deltaH = -pData->hScrollAmount;
  877.             if (currentPoint.v < 0)
  878.                 deltaV = pData->vScrollAmount;
  879.             if (currentPoint.v > qd.thePort->portRect.bottom)
  880.                 deltaV = -pData->vScrollAmount;
  881.                 
  882.             if ( (deltaH != 0) || (deltaV != 0) )
  883.                 {                
  884.                 if (deltaH)
  885.                     SetControlAndClipAmount(pData->hScroll, &deltaH);
  886.                 if (deltaV)
  887.                     SetControlAndClipAmount(pData->vScroll, &deltaV);
  888.  
  889.                 DoScrollContent(pWindow, pData, deltaH, deltaV);
  890.                 
  891.                 didJustScroll = true;
  892.                 }
  893.             }
  894.             
  895.             // map mouse into proper range
  896.             currentPoint.h += GetControlValue(pData->hScroll);
  897.             currentPoint.v += GetControlValue(pData->vScroll);
  898.     
  899.             // clip to the document size
  900.             if (currentPoint.h < 0)
  901.                 currentPoint.h = 0;
  902.             if (currentPoint.v < 0)
  903.                 currentPoint.v = 0;
  904.             if (currentPoint.h > pContent->right)
  905.                 currentPoint.h = pContent->right;
  906.             if (currentPoint.v > pContent->bottom)
  907.                 currentPoint.v = pContent->bottom;
  908.                 
  909.             // draw the new selection if it is time or we are about to 
  910.             // exit this loop
  911.             if ((MOVESELECTION(TickCount())) || (!Button()) || (didJustScroll) )
  912.                 {
  913.                 // first, erase any old selection we might have had
  914.                 DrawSelection(pData, pSelection, pPhase, false);
  915.  
  916.                 // make a rectangle out of the two points
  917.                 pSelection->left     = Min(currentPoint.h, clickPoint.h);
  918.                 pSelection->right     = Max(currentPoint.h, clickPoint.h);
  919.                 pSelection->top     = Min(currentPoint.v, clickPoint.v);
  920.                 pSelection->bottom     = Max(currentPoint.v, clickPoint.v);
  921.     
  922.                 // draw the new selection
  923.                 DrawSelection(pData, pSelection, pPhase, true);
  924.                 }
  925.             }
  926.         
  927.         // we handled the selection
  928.         anErr = eActionAlreadyHandled;
  929.         }
  930.         
  931.     return(anErr);
  932.     
  933. } // SelectContents
  934.  
  935. // --------------------------------------------------------------------------------------------------------------
  936. void DragAndDropArea(WindowRef pWindow, WindowDataPtr pData, EventRecord* event, Rect *pFrameRect)
  937. {
  938.     RgnHandle        hilightRgn;
  939.     Rect            r;
  940.     DragReference    theDrag;
  941.     OSErr            anErr = noErr;
  942.     
  943.     if (NewDrag(&theDrag) == noErr)
  944.         {
  945.         if (pData->pDragAddFlavors)
  946.             anErr = (*(pData->pDragAddFlavors)) (pWindow, pData, theDrag);
  947.         
  948.         if (anErr == noErr)
  949.             {
  950.             Rect    globalRect = *pFrameRect;
  951.             
  952.             hilightRgn = NewRgn();    
  953.             LocalToGlobal(&TopLeft(globalRect));
  954.             LocalToGlobal(&BotRight(globalRect));
  955.             RectRgn(hilightRgn, &globalRect);
  956.             SetDragItemBounds(theDrag, 1, &r);
  957.     
  958.             // turn the region from a fill into a frame
  959.             {    
  960.                 RgnHandle tempRgn = NewRgn();
  961.     
  962.                 CopyRgn(hilightRgn, tempRgn);
  963.                 InsetRgn(tempRgn, 1, 1);
  964.                 DiffRgn(hilightRgn, tempRgn, hilightRgn);
  965.                 DisposeRgn(tempRgn);
  966.             }
  967.             
  968.             TrackDrag(theDrag, event, hilightRgn);
  969.             DisposeDrag(theDrag);
  970.             DisposeRgn(hilightRgn);
  971.             }
  972.         }
  973.  
  974. } // DragAndDropArea
  975.  
  976. // --------------------------------------------------------------------------------------------------------------
  977. // WINDOW UTILITY ROUTINES
  978. // --------------------------------------------------------------------------------------------------------------
  979. #pragma segment Main
  980.  
  981. static void CalculateGrowIcon(WindowDataPtr pData, Rect * location)
  982. {
  983.     if (pData->vScroll)
  984.         location->top = (**pData->vScroll).contrlRect.bottom;
  985.     else
  986.         {
  987.         if (pData->hScroll)
  988.             location->top = (**pData->hScroll).contrlRect.top;
  989.         else
  990.             location->top = pData->theWindow.port.portRect.bottom - 15;
  991.         }
  992.         
  993.     if (pData->hScroll)
  994.         location->left = (**pData->hScroll).contrlRect.right;
  995.     else
  996.         {
  997.         if (pData->vScroll)
  998.             location->left = (**pData->vScroll).contrlRect.left;
  999.         else
  1000.             location->left = pData->theWindow.port.portRect.right - 15;
  1001.         }
  1002.         
  1003.     location->right = location->left + 16;
  1004.     location->bottom = location->top + 16;
  1005.     
  1006. } // CalculateGrowIcon
  1007.  
  1008. // --------------------------------------------------------------------------------------------------------------
  1009. #pragma segment Main
  1010.  
  1011. OSErr    AdjustScrollBars(WindowRef pWindow,
  1012.     Boolean moveControls,                 // might the controls have moved?
  1013.     Boolean didResize,                     // did we just resize the window?
  1014.     Boolean *needInvalidate)            // does the caller need to invalidate contents as a result?
  1015. {
  1016.     OSErr            anErr = noErr;
  1017.     LongRect        docRect;
  1018.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  1019.     Rect            growIconRect;
  1020.     
  1021.     if (needInvalidate)
  1022.         *needInvalidate = false;
  1023.  
  1024.     if (pData)
  1025.         {
  1026.         short    oldHMax, oldVMax;
  1027.         short    oldHValue, oldVValue;
  1028.         
  1029.         // cache current values, we'll force an update if we needed to change em!
  1030.         if (pData->hScroll)
  1031.             {
  1032.             oldHMax = GetControlMaximum(pData->hScroll);
  1033.             oldHValue = GetControlValue(pData->hScroll);
  1034.             }
  1035.         if (pData->vScroll)
  1036.             {
  1037.             oldVMax = GetControlMaximum(pData->vScroll);
  1038.             oldVValue = GetControlValue(pData->vScroll);
  1039.             }
  1040.             
  1041.         // if we have a grow box but not all controls we have to invalidate the grow bar areas
  1042.         // by caclulating them
  1043.         if ( (didResize) && (pData->hasGrow) )
  1044.             {
  1045.             // if we regrow without any scroll bars, we need to update the content area
  1046.             if ( (needInvalidate) && (pData->hScroll == nil) && (pData->vScroll == nil) )
  1047.                 *needInvalidate = true;
  1048.             
  1049.             // invalidate old grow bar areas
  1050.             if (pData->vScroll == nil)
  1051.                 {
  1052.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1053.                 growIconRect.left = pData->contentRect.right;
  1054.                 InvalRect(&growIconRect);
  1055.                 }
  1056.             if (pData->hScroll == nil)
  1057.                 {
  1058.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1059.                 growIconRect.top = pData->contentRect.bottom;
  1060.                 InvalRect(&growIconRect);
  1061.                 }
  1062.             
  1063.             // invalidate new grow bar areas
  1064.             if (pData->vScroll == nil)
  1065.                 {
  1066.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1067.                 growIconRect.left = growIconRect.right - kScrollBarSize;
  1068.                 InvalRect(&growIconRect);
  1069.                 }
  1070.             if (pData->hScroll == nil)
  1071.                 {
  1072.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1073.                 growIconRect.top = growIconRect.bottom - kScrollBarSize;
  1074.                 InvalRect(&growIconRect);
  1075.                 }
  1076.             }
  1077.             
  1078.         // if the controls need moving, recalculate the visible area
  1079.         if (moveControls)
  1080.             {
  1081.             pData->contentRect = GetWindowPort(pWindow)->portRect;
  1082.             if ((pData->hScroll) || (pData->hasGrow) )
  1083.                 pData->contentRect.bottom -= kScrollBarSize;
  1084.             if ((pData->vScroll) || (pData->hasGrow) )
  1085.                 pData->contentRect.right -= kScrollBarSize;
  1086.             }
  1087.             
  1088.         // before doing anything, make the controls invisible
  1089.         if (pData->hScroll)
  1090.             (**pData->hScroll).contrlVis = 0;    
  1091.         if (pData->vScroll)
  1092.             (**pData->vScroll).contrlVis = 0;
  1093.  
  1094.         // based on document and visiable area, adjust possible control values
  1095.         if ( (pData->pGetDocumentRect) && ((pData->hScroll) || (pData->vScroll)) )
  1096.             {
  1097.             // let the object calc the size and content if it wishes to
  1098.             anErr = (*(pData->pGetDocumentRect)) (pWindow, pData, &docRect, false);
  1099.             if (anErr == noErr)
  1100.                 {
  1101.                 short    amountOver;
  1102.                 short    newMax;
  1103.                 
  1104.                 amountOver = (docRect.right - docRect.left) - (pData->contentRect.right - pData->contentRect.left);
  1105.                 if     (
  1106.                     (pData->hScroll) &&
  1107.                     (amountOver > 0)
  1108.                     )
  1109.                     newMax = amountOver;
  1110.                 else
  1111.                     newMax = 0;
  1112.     
  1113.                 if (pData->hScroll)
  1114.                     {
  1115.                     if (GetControlValue(pData->hScroll) > newMax)
  1116.                         {
  1117.                         if (needInvalidate)
  1118.                             *needInvalidate = true;
  1119.                         }
  1120.                     SetControlMaximum(pData->hScroll, newMax);
  1121.                     }
  1122.                 
  1123.                 amountOver = (docRect.bottom - docRect.top) - (pData->contentRect.bottom - pData->contentRect.top);
  1124.                 if     (
  1125.                     (pData->vScroll) &&
  1126.                     (amountOver > 0)
  1127.                     )
  1128.                     newMax = amountOver;
  1129.                 else
  1130.                     newMax = 0;
  1131.                     
  1132.                 if (pData->vScroll)
  1133.                     {
  1134.                     if (GetControlValue(pData->vScroll) > newMax)
  1135.                         {
  1136.                         if (needInvalidate)
  1137.                             *needInvalidate = true;
  1138.                         }
  1139.                     SetControlMaximum(pData->vScroll, newMax);
  1140.                     }
  1141.                 }
  1142.             }
  1143.             
  1144.         // then, if the controls need moving, we move them and inval the old
  1145.         // and new locations
  1146.         if (moveControls)
  1147.             {
  1148.             // if we have grow box we invalidate the old grow location
  1149.             if ( pData->hasGrow) 
  1150.                 {
  1151.                 CalculateGrowIcon(pData, &growIconRect);
  1152.                 InvalRect(&growIconRect);
  1153.                 }
  1154.                 
  1155.             if (pData->hScroll)
  1156.                 {
  1157.                 short    widthAdjust;
  1158.                 
  1159.                 if ((pData->vScroll) || (pData->hasGrow))
  1160.                     widthAdjust = -kGrowScrollAdjust;
  1161.                 else
  1162.                     widthAdjust = -1;
  1163.                     
  1164.                 growIconRect = (**pData->hScroll).contrlRect;
  1165.                 InvalRect(&growIconRect);
  1166.                 
  1167.                 MoveControl(pData->hScroll, pData->hScrollOffset-1, GetWindowPort(pWindow)->portRect.bottom - kScrollBarSize);
  1168.                 SizeControl(pData->hScroll, (GetWindowPort(pWindow)->portRect.right - 
  1169.                             GetWindowPort(pWindow)->portRect.left) + widthAdjust - pData->hScrollOffset,
  1170.                             16);
  1171.  
  1172.                 growIconRect = (**pData->hScroll).contrlRect;
  1173.                 InvalRect(&growIconRect);
  1174.                 }
  1175.  
  1176.             if (pData->vScroll)
  1177.                 {
  1178.                 short    heightAdjust;
  1179.                 
  1180.                 if ((pData->hScroll) || (pData->hasGrow))
  1181.                     heightAdjust = -kGrowScrollAdjust;
  1182.                 else
  1183.                     heightAdjust = -1;
  1184.                     
  1185.                 growIconRect = (**pData->vScroll).contrlRect;
  1186.                 InvalRect(&growIconRect);
  1187.  
  1188.                 MoveControl(pData->vScroll, GetWindowPort(pWindow)->portRect.right - kScrollBarSize, pData->vScrollOffset-1);
  1189.                 SizeControl(pData->vScroll, 16,
  1190.                             (GetWindowPort(pWindow)->portRect.bottom - 
  1191.                             GetWindowPort(pWindow)->portRect.top) + heightAdjust - pData->vScrollOffset);
  1192.                 growIconRect = (**pData->vScroll).contrlRect;
  1193.                 InvalRect(&growIconRect);
  1194.                 }
  1195.                 
  1196.             // if we have scroll bars, update the grow icon
  1197.             if ( pData->hasGrow )
  1198.                 {
  1199.                 CalculateGrowIcon(pData, &growIconRect);
  1200.                 InvalRect(&growIconRect);
  1201.                 }
  1202.             
  1203.             }
  1204.  
  1205.         // let the document adjust anything it needs to
  1206.         if (pData->pAdjustSize)
  1207.             anErr = (*(pData->pAdjustSize)) (pWindow, pData, &didResize);
  1208.             
  1209.         if ((didResize) && (needInvalidate))
  1210.             *needInvalidate = true;
  1211.  
  1212.  
  1213.         if ( ((WindowPeek) pWindow)->hilited )
  1214.             {
  1215.             // after doing something, make the controls visible
  1216.             if (pData->hScroll)
  1217.                 {
  1218.                 if ((oldHMax != GetControlMaximum(pData->hScroll)) || (oldHValue != GetControlValue(pData->hScroll)) )
  1219.                     ShowControl(pData->hScroll);
  1220.                 else
  1221.                     (**pData->hScroll).contrlVis = 0xFF;    
  1222.                 }
  1223.             if (pData->vScroll)
  1224.                 {
  1225.                 if ((oldVMax != GetControlMaximum(pData->vScroll)) || (oldVValue != GetControlValue(pData->vScroll)) )
  1226.                     ShowControl(pData->vScroll);
  1227.                 else
  1228.                     (**pData->vScroll).contrlVis = 0xFF;
  1229.                 }
  1230.             }
  1231.  
  1232.         }
  1233.         
  1234.     return anErr;
  1235.     
  1236. } // AdjustScrollBars
  1237.  
  1238. // --------------------------------------------------------------------------------------------------------------
  1239. // MENU UTILITY ROUTINES
  1240. // --------------------------------------------------------------------------------------------------------------
  1241. #pragma segment Main
  1242.  
  1243. Boolean CommandToIDs(short commandID, short * menuID, short *itemID)
  1244. {
  1245.  
  1246.     short    ** commandHandle;
  1247.     short    whichMenu;
  1248.     short    oldResFile = CurResFile();
  1249.     Boolean    returnValue = false;
  1250.     
  1251.     UseResFile(gApplicationResFile);
  1252.     for (whichMenu = mApple; whichMenu <= mLastMenu; whichMenu++)
  1253.         {
  1254.         commandHandle = (short**) Get1Resource('MCMD', whichMenu);
  1255.         if (commandHandle)
  1256.             {
  1257.             short    * pCommands = *commandHandle;
  1258.             short    commandIndex;
  1259.             short    numCommands = pCommands[0];
  1260.             
  1261.             for (commandIndex = 1; commandIndex <= numCommands; ++commandIndex)
  1262.                 if (pCommands[commandIndex] == commandID)
  1263.                     {
  1264.                     *menuID = whichMenu;
  1265.                     *itemID = commandIndex;
  1266.                     
  1267.                     returnValue = (commandIndex == numCommands);
  1268.                     }
  1269.             }    
  1270.         }
  1271.         
  1272.     UseResFile(oldResFile);
  1273.     
  1274.     return returnValue;
  1275.     
  1276. } // CommandToIDs
  1277.  
  1278. // --------------------------------------------------------------------------------------------------------------
  1279. #pragma segment Main
  1280.  
  1281. Boolean IsCommandEnabled(short commandID)
  1282. /*
  1283.     returns true if a given command is currently enabled
  1284. */
  1285. {
  1286.     short        whichMenu, whichItem;
  1287.     MenuHandle    menu;
  1288.     
  1289.     CommandToIDs(commandID, &whichMenu, &whichItem);
  1290.     menu = GetMenuHandle(whichMenu);
  1291.     
  1292.     if ((**menu).enableFlags & (1 << whichItem))
  1293.         return(true);
  1294.     
  1295.     return(false);
  1296.     
  1297. } // IsCommandEnabled
  1298.  
  1299. // --------------------------------------------------------------------------------------------------------------
  1300. #pragma segment Main
  1301.  
  1302. void EnableCommand(short commandID)
  1303. /*
  1304.     Given a command ID, enables the first menu item with that command ID.
  1305.     
  1306.     If the command table for a given menu is less than the number of items in the menu,
  1307.     and the command being enabled is the last item in the command table, then all
  1308.     items from there on down are also enabled.  This is useful for menus that get
  1309.     appended to, such as the desk accessory list, font list, or speaking voices list.
  1310. */
  1311. {
  1312.     short    whichMenu;
  1313.     short    whichItem;
  1314.     
  1315.     if (CommandToIDs(commandID, &whichMenu, &whichItem))
  1316.         {
  1317.         short        i;
  1318.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1319.         
  1320.         if (menu)
  1321.             {
  1322.             short        numItems = CountMItems(menu);
  1323.             
  1324.             for (i = whichItem; i <= numItems; ++i)
  1325.                 EnableItem(menu, i);
  1326.             }
  1327.         }
  1328.     else
  1329.         {
  1330.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1331.  
  1332.         if (menu)
  1333.             EnableItem(menu, whichItem);
  1334.         }
  1335.         
  1336. } // EnableCommand
  1337.  
  1338. // --------------------------------------------------------------------------------------------------------------
  1339. #pragma segment Main
  1340.  
  1341. void ChangeCommandName(short commandID, short resourceID, short resourceIndex)
  1342. {
  1343.     short        whichMenu;
  1344.     short        whichItem;
  1345.     MenuHandle    menu;
  1346.     
  1347.     // figure out how this command maps into the menu bar
  1348.     CommandToIDs(commandID, &whichMenu, &whichItem);
  1349.     menu = GetMenuHandle(whichMenu);
  1350.     
  1351.     // then make this item into the requested new string
  1352.     {
  1353.     Str255        theString;
  1354.     
  1355.     GetIndString(theString, resourceID, resourceIndex);
  1356.     SetMenuItemText(menu, whichItem, theString);
  1357.     }
  1358.     
  1359. } // ChangeCommandName
  1360.  
  1361. // --------------------------------------------------------------------------------------------------------------
  1362. #pragma segment Main
  1363.  
  1364. void EnableCommandCheck(short commandID, Boolean check)
  1365. {
  1366.  
  1367.     short    whichMenu;
  1368.     short    whichItem;
  1369.     
  1370.     if (CommandToIDs(commandID, &whichMenu, &whichItem))
  1371.         {
  1372.         short        i;
  1373.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1374.         short        numItems = CountMItems(menu);
  1375.         
  1376.         for (i = whichItem; i <= numItems; ++i)
  1377.             {
  1378.             EnableItem(menu, i);
  1379.             CheckItem(menu, i, check);
  1380.             }
  1381.         }
  1382.     else
  1383.         {
  1384.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1385.  
  1386.         EnableItem(menu, whichItem);
  1387.         CheckItem(menu, whichItem, check);
  1388.         }
  1389.         
  1390. } // EnableCommandCheck
  1391.  
  1392.  
  1393. // --------------------------------------------------------------------------------------------------------------
  1394. #pragma segment Main
  1395.  
  1396. void EnableCommandCheckStyle(short commandID, Boolean check, short style)
  1397. {
  1398.  
  1399.     short    whichMenu;
  1400.     short    whichItem;
  1401.     
  1402.     if (CommandToIDs(commandID, &whichMenu, &whichItem))
  1403.         {
  1404.         short        i;
  1405.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1406.         short        numItems = CountMItems(menu);
  1407.         
  1408.         for (i = whichItem; i <= numItems; ++i)
  1409.             {
  1410.             EnableItem(menu, i);
  1411.             CheckItem(menu, i, check);
  1412.             SetItemStyle(menu, i, style);
  1413.             }
  1414.         }
  1415.     else
  1416.         {
  1417.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1418.  
  1419.         EnableItem(menu, whichItem);
  1420.         CheckItem(menu, whichItem, check);
  1421.         SetItemStyle(menu, whichItem, style);
  1422.         }
  1423.         
  1424. } // EnableCommandCheckStyle
  1425.  
  1426. // --------------------------------------------------------------------------------------------------------------
  1427. #pragma segment Main
  1428.  
  1429. void AdjustMenus(WindowRef pWindow, Boolean editDialogs, Boolean forceTitlesOn)
  1430. {
  1431.     Boolean                 wasEnabled[mNumberMenus];    // Old state of menus
  1432.     short                    whichMenu;                    // for stepping through menus
  1433.     MenuHandle                menu;                        // for reading in menu IDs
  1434.     WindowDataPtr             pData = GetWindowInfo(pWindow);
  1435.     
  1436.     // Step through all of the menus 
  1437.     for (whichMenu = mApple; whichMenu <= mLastMenu; whichMenu++)
  1438.         {
  1439.         // Save the old state of the menu title 
  1440.         menu = GetMenuHandle(whichMenu);
  1441.         if (menu)                                // because contents menu may not be around
  1442.             {
  1443.             if (forceTitlesOn)                
  1444.                 wasEnabled[mLastMenu - whichMenu] = false;
  1445.             else
  1446.                 wasEnabled[mLastMenu - whichMenu] = (((**menu).enableFlags && 1) == 1);
  1447.             
  1448.             // Disable the entire menu 
  1449.             (**menu).enableFlags = 0;        
  1450.             }
  1451.         }
  1452.     
  1453.     // select all, unless someone else changes it
  1454.     ChangeCommandName(cSelectAll, kMiscStrings, iSelectAllCommand);
  1455.  
  1456.     // if we have NO windows, or the current window is one we understand
  1457.     if ((pWindow == nil) || (pData))
  1458.         {
  1459.         // enable the default commands
  1460.         EnableCommand(cAbout);
  1461.         EnableCommand(cDeskAccessory);
  1462.         
  1463.         EnableCommand(cNew);
  1464.         EnableCommand(cOpen);
  1465.         EnableCommand(cQuit);
  1466.     
  1467.         EnableCommand(cShowClipboard);
  1468.         }
  1469.     else
  1470.         {
  1471.         // it's printing or a dialog, so enable cut/copy/paste
  1472.         if (editDialogs)
  1473.             {
  1474.             EnableCommand(cCut);
  1475.             EnableCommand(cCopy);
  1476.             EnableCommand(cPaste);
  1477.             EnableCommand(cClear);
  1478.             }
  1479.         
  1480.         // and desk accs too!        
  1481.         EnableCommand(cDeskAccessory);
  1482.  
  1483.         }
  1484.         
  1485.     if ( (pWindow) && (pData) )
  1486.         {
  1487.         // all windows can be closed
  1488.         if (FrontWindow())
  1489.             EnableCommand(cClose);
  1490.  
  1491.         // changed documents can be saved, but only if the file is open for write
  1492.         if (     (pData->changed) && 
  1493.                 ((pData->isWritable) || (pData->dataRefNum == -1)) )
  1494.             EnableCommand(cSave);
  1495.         
  1496.         // objects with a print method can be printed and page setup-ed
  1497.         if (pData->pPrintPage)
  1498.             {
  1499.             EnableCommand(cPrint);
  1500.             EnableCommand(cPageSetup);
  1501.             EnableCommand(cPrintOneCopy);
  1502.             }
  1503.             
  1504.         // let object enable anything else that needs to be enabled
  1505.         if (pData->pAdjustMenus)
  1506.             (*(pData->pAdjustMenus)) (pWindow, pData);
  1507.         }
  1508.         
  1509.     // Now determine if any of the menus have changed state
  1510.     {
  1511.     Boolean gotToRedraw = false;
  1512.     
  1513.     for (whichMenu = mApple; whichMenu <= mLastMenu; ++whichMenu)
  1514.         {
  1515.         menu = GetMenuHandle(whichMenu);
  1516.     
  1517.         if (menu)        // because contents menu may not be around
  1518.             {
  1519.             // If any of the menu is enabled 
  1520.             if ((**menu).enableFlags != 0)
  1521.                 {
  1522.                 // Make sure to turn on the menu title 
  1523.                 (**menu).enableFlags |= 1;
  1524.                 }
  1525.                 
  1526.             /*     If this new state is different than the saved state, then the menu bar
  1527.                 will need to be redrawn */
  1528.             if (wasEnabled[mLastMenu - whichMenu] != ((**menu).enableFlags && 1))
  1529.                 {
  1530.                 gotToRedraw = true;
  1531.                 }
  1532.             }
  1533.         }
  1534.         
  1535.     // And if any titles have changed state, redraw them 
  1536.     if (gotToRedraw)
  1537.         DrawMenuBar();
  1538.     }
  1539.         
  1540. } // AdjustMenus
  1541.  
  1542. // --------------------------------------------------------------------------------------------------------------
  1543. // PRINTING UTILITY ROUTINES
  1544. // --------------------------------------------------------------------------------------------------------------
  1545. #pragma segment Printing
  1546.  
  1547. Boolean IsSomewhereInRectangle(gxRectangle *pContainer, gxRectangle *pShape)
  1548. /*
  1549.     Calculates this by saying the rectangle doesn't intersect at ALL,
  1550.     and then NOTs that expression.
  1551. */
  1552. {
  1553.     return
  1554.         (!(
  1555.         pShape->top > pContainer->bottom ||
  1556.         pShape->bottom < pContainer->top ||
  1557.         pShape->left > pContainer->right ||
  1558.         pShape->right < pContainer->left
  1559.         ));
  1560.         
  1561. } // IsSomewhereInRectangle
  1562.  
  1563. // --------------------------------------------------------------------------------------------------------------
  1564. #pragma segment Printing
  1565.     
  1566. static OSErr SimpleCatchShape(gxShape newShape, CatchRefCon * pRefCon)
  1567. {
  1568.     Boolean                addShape = false;
  1569.     gxGraphicsError        anErr;
  1570.  
  1571.     // did the user abort printing?
  1572.     anErr = GXGetJobError(pRefCon->theJob);
  1573.     if (anErr != noErr)
  1574.         return anErr;
  1575.         
  1576.     switch (GXGetShapeType(newShape))
  1577.         {
  1578.         
  1579.         // if we have a layout, turn off justification so that we can get
  1580.         // better looking morph effects, and also enable the default features
  1581.         // of the layout.  However, we can only do this if the layout is
  1582.         // in a script system where the translation wasn't done by a Print Action Hook.
  1583.         // For now, this means only smRoman styles.
  1584.         case gxLayoutType:
  1585.             if (pRefCon->doLayout)
  1586.                 {
  1587.                 Boolean            enableLayout = pRefCon->doLayout;
  1588.         
  1589.                 gxStyle            * theStyles;
  1590.                 long            styleCount, index;
  1591.                 
  1592.                 GXGetLayout(newShape, nil,
  1593.                             &styleCount, nil, nil,    // style runs
  1594.                             nil, nil, nil,    // run levels
  1595.                             nil, nil);
  1596.                 theStyles = (gxStyle*) NewPtr(sizeof(gxStyle)*styleCount);
  1597.                 if (theStyles)
  1598.                     {
  1599.                     GXGetLayout(newShape, nil,
  1600.                                 &styleCount, nil, theStyles,    // style runs
  1601.                                 nil, nil, nil,    // run levels
  1602.                                 nil, nil);
  1603.                     
  1604.                     enableLayout = true;
  1605.                     for (index = 0; index < styleCount; ++index)
  1606.                         {
  1607.                         gxFontScript theScript;
  1608.                         gxFontPlatform thePlatform = GXGetStyleEncoding(theStyles[index], &theScript, nil);
  1609.                         
  1610.                         if ((thePlatform != gxMacintoshPlatform) || (theScript != gxRomanScript))
  1611.                             enableLayout = false;
  1612.                         }
  1613.                             
  1614.                     if (enableLayout)
  1615.                         for (index = 0; index < styleCount; ++index)
  1616.                             {
  1617.                             gxRunControls theControls;
  1618.                             
  1619.                             // re-enable run control features
  1620.                             GXGetStyleRunControls(theStyles[index], &theControls);
  1621.                             theControls.flags = 0;
  1622.                             theControls.track = 0;
  1623.                             theControls.hangingInhibitFactor = 0;
  1624.                             theControls.kerningInhibitFactor = 0;
  1625.                             GXSetStyleRunControls(theStyles[index], &theControls);
  1626.                             
  1627.                             // and turn back on default features
  1628.                             GXSetStyleRunFeatures(theStyles[index], 0, nil);
  1629.                             }
  1630.                         
  1631.                     DisposePtr((Ptr) theStyles);
  1632.                     }
  1633.         
  1634.                 if ( (enableLayout) && (GetSysDirection() == 0) )
  1635.                     {
  1636.                     // turn off justification
  1637.                     {
  1638.                     gxLayoutOptions    layoutOptions;
  1639.             
  1640.                     // get the current layout options
  1641.                     layoutOptions.baselineRec = nil;
  1642.                     GXGetLayout(newShape, nil,
  1643.                                 nil, nil, nil,    // style runs
  1644.                                 nil, nil, nil,    // run levels
  1645.                                 &layoutOptions, nil);
  1646.                         
  1647.                     // setting width to zero allows lines to float, but causes multi-styled
  1648.                     // lines (separate layouts from the translation process) to run into
  1649.                     // one another, so we can't do that
  1650.                     //layoutOptions.width = 0;
  1651.                     layoutOptions.just = gxNoJustification;
  1652.                     GXSetLayout(newShape, 
  1653.                                 0, nil, nil,    // text runs
  1654.                                 0, nil, nil,    // style runs
  1655.                                 0, nil, nil, // run levels
  1656.                                 &layoutOptions, nil);
  1657.                     }
  1658.                     
  1659.                     // un-clip the shape left and right so we can see things like hanging puncs.
  1660.                         {
  1661.                         gxShape            newClip;
  1662.                         gxRectangle        bounds;
  1663.                         
  1664.                         newClip = GXGetShapeClip(newShape);
  1665.                         GXGetShapeLocalBounds(newClip, &bounds);
  1666.                         GXDisposeShape(newClip);
  1667.                         
  1668.                         bounds.left     = gxNegativeInfinity;
  1669.                         bounds.right     = gxPositiveInfinity;
  1670.                         newClip = GXNewRectangle(&bounds);
  1671.                         GXSetShapeClip(newShape, newClip);
  1672.                         GXDisposeShape(newClip);
  1673.                         }
  1674.                     }
  1675.                 
  1676.                 }
  1677.  
  1678.             // if we aren't forming layouts, we'll go ahead and check for this being on
  1679.             // the shape.  But for pure text, we'll just always add the text.  TextEdit
  1680.             // is pretty good about pre-clipping for us.
  1681.             if (!pRefCon->doLayout)
  1682.                 {
  1683.                 gxRectangle    bounds;
  1684.                 
  1685.                 GXGetShapeLocalBounds(newShape, &bounds);
  1686.                 if (IsSomewhereInRectangle(&pRefCon->thePageRectangle, &bounds))
  1687.                     addShape = true;
  1688.                 }
  1689.             else
  1690.                 addShape = true;
  1691.             break;
  1692.         
  1693.         // never add these for text case
  1694.         case gxRectangleType:
  1695.             if (!pRefCon->doLayout)
  1696.                 addShape = true;
  1697.             break;
  1698.             
  1699.         // always add these shapes if we see any because we don't know how to filter them
  1700.         case gxEmptyType:
  1701.         case gxFullType:
  1702.         case gxPictureType:
  1703.             addShape = true;
  1704.             break;
  1705.             
  1706.         default:
  1707.             {
  1708.             gxRectangle    bounds;
  1709.             
  1710.             GXGetShapeLocalBounds(newShape, &bounds);
  1711.             if (IsSomewhereInRectangle(&pRefCon->thePageRectangle, &bounds))
  1712.                 addShape = true;
  1713.             }
  1714.             break;
  1715.             
  1716.         } // switch
  1717.         
  1718.     if (addShape)
  1719.         GXSetPictureParts(pRefCon->thePage, 0, 0, 1, &newShape, nil, nil, nil);    /* Add shape */
  1720.     
  1721.     GXGetGraphicsError(&anErr);
  1722.     
  1723.     if (anErr == noErr)
  1724.         {
  1725.         GXIdleJob(pRefCon->theJob);
  1726.         anErr = GXGetJobError(pRefCon->theJob);
  1727.         }
  1728.         
  1729.     return anErr;
  1730.     
  1731. } // SimpleCatchShape
  1732.  
  1733. #if GENERATINGCFM
  1734.     static RoutineDescriptor gSimpleCatchShapeRD = BUILD_ROUTINE_DESCRIPTOR(uppgxShapeSpoolProcInfo, SimpleCatchShape);
  1735.     static gxShapeSpoolUPP gSimpleCatchShape = &gSimpleCatchShapeRD;
  1736. #else
  1737.     static gxShapeSpoolUPP gSimpleCatchShape = NewgxShapeSpoolProc(SimpleCatchShape);
  1738. #endif
  1739.  
  1740. // --------------------------------------------------------------------------------------------------------------
  1741. #pragma segment Printing
  1742.  
  1743. static OSErr CompleteSpoolFileMessage( gxSpoolFile theSpoolFile )
  1744. {
  1745.     OSErr            anErr         = noErr;
  1746.     Handle            hIcon;
  1747.     short            sourceIcon     = 0;
  1748.     short            docIcon     = 132;
  1749.     WindowDataPtr    pData         = GXGetJobRefCon(GXGetJob());
  1750.     
  1751.     // for some file types, we can supply a nicer icon for the Finder to display
  1752.     // within the queue when this document is printing
  1753.     switch (pData->originalFileType)
  1754.         {
  1755.         case 'TEXT':
  1756.         case 'sEXT':
  1757.             sourceIcon = kTextIcon;
  1758.             break;
  1759.                         
  1760.         case 'ttro':
  1761.             sourceIcon = kReadOnlyIcon;
  1762.             break;
  1763.  
  1764.         case 'PICT':
  1765.             sourceIcon = kPICTIcon;
  1766.             break;
  1767.         }
  1768.         
  1769.     if (sourceIcon != 0)
  1770.         {
  1771.         hIcon = GetResource('ICN#', sourceIcon);
  1772.         if (hIcon != nil) 
  1773.             {
  1774.             HNoPurge(hIcon);
  1775.             DetachResource(hIcon);
  1776.             anErr = Send_GXSpoolResource (theSpoolFile, hIcon, 'ICN#', docIcon);
  1777.             }
  1778.         nrequire(anErr, SpoolOneBit);
  1779.         
  1780.         hIcon = GetResource('icl4', sourceIcon);
  1781.         if (hIcon != nil) 
  1782.             {
  1783.             HNoPurge(hIcon);
  1784.             DetachResource(hIcon);
  1785.             anErr = Send_GXSpoolResource (theSpoolFile, hIcon, 'icl4', docIcon);
  1786.             }
  1787.         nrequire(anErr, SpoolFourBit);
  1788.     
  1789.         hIcon = GetResource('icl8', sourceIcon);
  1790.         if (hIcon != nil) 
  1791.             {
  1792.             HNoPurge(hIcon);
  1793.             DetachResource(hIcon);
  1794.             anErr = Send_GXSpoolResource (theSpoolFile, hIcon, 'icl8', docIcon);
  1795.             }
  1796.         nrequire(anErr, SpoolEightBit);
  1797.         }
  1798.     
  1799.     anErr = Forward_GXCompleteSpoolFile( theSpoolFile );
  1800.     
  1801. // FALL THROUGH EXCEPTION HANDLING
  1802. SpoolOneBit:
  1803. SpoolFourBit:
  1804. SpoolEightBit:
  1805.  
  1806.     return anErr;
  1807.     
  1808. } // CompleteSpoolFileMessage
  1809.  
  1810. #if GENERATINGCFM
  1811.     static RoutineDescriptor gCompleteSpoolFileMessageRD = BUILD_ROUTINE_DESCRIPTOR(uppGXCompleteSpoolFileProcInfo, CompleteSpoolFileMessage);
  1812.     static GXCompleteSpoolFileUPP gCompleteSpoolFileMessage = &gCompleteSpoolFileMessageRD;
  1813. #else
  1814.     static GXCompleteSpoolFileUPP gCompleteSpoolFileMessage = NewGXCompleteSpoolFileProc(CompleteSpoolFileMessage);
  1815. #endif
  1816.  
  1817. // --------------------------------------------------------------------------------------------------------------
  1818. #pragma segment Printing
  1819.  
  1820. static OSErr PrintingEventMessage(EventRecord *event, Boolean filter)
  1821. {
  1822.     OSErr     anErr = noErr;
  1823.     GrafPtr    curPort;
  1824.     
  1825.     GetPort(&curPort);
  1826.     if (filter == false) 
  1827.         {
  1828.         switch ( event->what ) 
  1829.             {
  1830.             case mouseDown:
  1831.             case keyDown:
  1832.             case autoKey:
  1833.                 break;
  1834.                 
  1835.             case activateEvt:
  1836.             case updateEvt:
  1837.             default:
  1838.                 HandleEvent(event);
  1839.                 break;
  1840.             }
  1841.         }
  1842.     
  1843.     anErr = Forward_GXPrintingEvent(event, filter);
  1844.     SetPort(curPort);
  1845.     
  1846.     return anErr;
  1847.     
  1848. } // PrintingEventMessage
  1849.  
  1850. #if GENERATINGCFM
  1851.     static RoutineDescriptor gPrintingEventMessageRD = BUILD_ROUTINE_DESCRIPTOR(uppGXPrintingEventProcInfo, PrintingEventMessage);
  1852.     static GXPrintingEventUPP gPrintingEventMessage = &gPrintingEventMessageRD;
  1853. #else
  1854.     static GXPrintingEventUPP gPrintingEventMessage = NewGXPrintingEventProc(PrintingEventMessage);
  1855. #endif
  1856.  
  1857.  
  1858.  
  1859. // --------------------------------------------------------------------------------------------------------------
  1860. // FILE UTILITY ROUTINES
  1861. // --------------------------------------------------------------------------------------------------------------
  1862. #pragma segment Main
  1863.  
  1864. static Boolean BringToFrontIfOpen(FSSpecPtr pSpec)
  1865. {
  1866.     WindowRef        pWindow;
  1867.     
  1868.     pWindow = FrontWindow();
  1869.     while (pWindow)
  1870.         {
  1871.         WindowDataPtr pData = GetWindowInfo(pWindow);
  1872.         
  1873.         if (
  1874.             (pData) &&
  1875.             (pData->fileSpec.vRefNum == pSpec->vRefNum) &&
  1876.             (pData->fileSpec.parID == pSpec->parID) &&
  1877.             EqualString(pData->fileSpec.name, pSpec->name, false, false)
  1878.             )
  1879.             {
  1880.             SelectWindow(pWindow);
  1881.             return true;
  1882.             }
  1883.             
  1884.         pWindow = GetNextWindow(pWindow);
  1885.         }
  1886.         
  1887.     return false;
  1888.     
  1889. } // BringToFrontIfOpen
  1890.  
  1891. // --------------------------------------------------------------------------------------------------------------
  1892. #pragma segment Main
  1893.  
  1894. static Boolean BringToFrontIfExists(ResType windowKind)
  1895. {
  1896.     WindowRef        pWindow;
  1897.     
  1898.     pWindow = FrontWindow();
  1899.     while (pWindow)
  1900.         {
  1901.         WindowDataPtr pData = GetWindowInfo(pWindow);
  1902.         
  1903.         if ((pData) && (pData->windowKind == windowKind))
  1904.             {
  1905.             SelectWindow(pWindow);
  1906.             return true;
  1907.             }
  1908.             
  1909.         pWindow = GetNextWindow(pWindow);
  1910.         }
  1911.         
  1912.     return false;
  1913.     
  1914. } // BringToFrontIfExists
  1915.  
  1916. // --------------------------------------------------------------------------------------------------------------
  1917. // MAIN SIMPLETEXT ROUTINES
  1918. // --------------------------------------------------------------------------------------------------------------
  1919. #pragma segment Main
  1920.  
  1921. static OSErr MakeNewWindow(ResType windowKind, FSSpecPtr fileSpec, OSType fileType, Boolean *pWasAlreadyOpen)
  1922. {
  1923.     OSErr                anErr = fnfErr;
  1924.     PreflightRecord        thePreflight;
  1925.     PreflightWindowProc    pPreflight = nil;
  1926.     WindowRef            pWindow;
  1927.     WindowDataPtr        pData;
  1928.     
  1929.     // require a certain amount of RAM free before we allow the new window to be created
  1930.     if (FreeMem() < kRAMNeededForNew)
  1931.         anErr = memFullErr;
  1932.         
  1933.     // <50> if we already have a document open from this file, bring the window to the
  1934.     // front and return with no error
  1935.     if ( (fileSpec) && (fileType != 'sEXT') && (BringToFrontIfOpen(fileSpec)) )
  1936.         {
  1937.         if (pWasAlreadyOpen)
  1938.             *pWasAlreadyOpen = true;
  1939.         anErr = noErr;
  1940.         return(anErr);
  1941.         }
  1942.     if (pWasAlreadyOpen)
  1943.         *pWasAlreadyOpen = false;
  1944.     if (anErr != fnfErr)
  1945.         {
  1946.         nrequire(anErr, SanityCheckFailed);
  1947.         }
  1948.         
  1949.     // initialize our behavior
  1950.     thePreflight.continueWithOpen     = true;
  1951.     thePreflight.resourceID         = kDefaultWindowID;
  1952.     thePreflight.wantHScroll         = false;
  1953.     thePreflight.wantVScroll         = false;
  1954.     thePreflight.storageSize         = sizeof(WindowDataRecord);
  1955.     thePreflight.makeProcPtr         = nil;
  1956.     thePreflight.openKind            = fsRdPerm;
  1957.     thePreflight.needResFork        = false;
  1958.     thePreflight.doZoom                = false;
  1959.     thePreflight.fileType            = fileType;
  1960.     
  1961.     switch (windowKind)
  1962.         {
  1963.         case kAboutWindow:
  1964.             pPreflight = AboutPreflightWindow;
  1965.             break;
  1966.  
  1967.         case kPICTWindow:
  1968.             pPreflight = PICTPreflightWindow;
  1969.             break;
  1970.  
  1971.         case kMovieWindow:
  1972.             pPreflight = MoviePreflightWindow;
  1973.             break;
  1974.  
  1975.         case kClipboardWindow:
  1976.             pPreflight = ClipboardPreflightWindow;
  1977.             break;
  1978.  
  1979.         case kTextWindow:
  1980.             pPreflight = TextPreflightWindow;
  1981.             break;
  1982.  
  1983.         case kGXWindow:
  1984.             pPreflight = GXPreflightWindow;
  1985.             break;
  1986.  
  1987.         case kThreeDWindow:
  1988.             pPreflight = ThreeDPreflightWindow;
  1989.             break;
  1990.         }
  1991.     
  1992.     // preflight the window    
  1993.     if (pPreflight)
  1994.         anErr = (*pPreflight) (&thePreflight);
  1995.     nrequire(anErr, PreflightFailed);
  1996.     
  1997.     if (thePreflight.continueWithOpen)
  1998.         {
  1999.         // allocate a place for the window
  2000.         pData = (WindowDataPtr)NewPtrClear(thePreflight.storageSize);
  2001.         anErr = MemError();
  2002.         nrequire(anErr, FailedToAllocateWindow);
  2003.         
  2004.         // then actually create the window
  2005.         if (gMachineInfo.theEnvirons.hasColorQD)
  2006.             pWindow = (WindowRef)GetNewCWindow(thePreflight.resourceID, pData, (WindowPtr)-1);
  2007.         else
  2008.             pWindow = (WindowRef)GetNewWindow(thePreflight.resourceID, pData, (WindowPtr)-1);
  2009.         if (!pWindow) anErr = memFullErr;
  2010.         nrequire(anErr, NewWindowFailed);
  2011.         SetWRefCon(pWindow, (long) pData);
  2012.                 
  2013.         // zoom the rectangle to big size on this monitor 
  2014.         // based upon which scroll bars they want
  2015.         {
  2016.         Rect    rect = GetWindowPort(pWindow)->portRect;
  2017.         Rect    bigRect;
  2018.         
  2019.         if (gMachineInfo.theEnvirons.hasColorQD)
  2020.             bigRect = (**GetMainDevice()).gdRect;
  2021.         else
  2022.             bigRect = qd.screenBits.bounds;
  2023.         bigRect.top += GetMBarHeight() * 2;
  2024.         bigRect.left += 4;
  2025.         bigRect.bottom -= 4;
  2026.         bigRect.right -= 65;
  2027.         
  2028.         SetPort((GrafPtr) GetWindowPort(pWindow));
  2029.         LocalToGlobal(&TopLeft(rect));
  2030.         LocalToGlobal(&BotRight(rect));
  2031.         
  2032.         if ( (thePreflight.wantHScroll) || (thePreflight.doZoom) )
  2033.             {
  2034.             rect.left = bigRect.left;
  2035.             rect.right = bigRect.right;
  2036.             }
  2037.             
  2038.         if ( (thePreflight.wantVScroll) || (thePreflight.doZoom) )
  2039.             {
  2040.             rect.top = bigRect.top;
  2041.             rect.bottom = bigRect.bottom;
  2042.             }
  2043.         
  2044.         MoveWindow(pWindow, rect.left, rect.top, false);
  2045.         SizeWindow(pWindow, rect.right - rect.left, rect.bottom - rect.top, false);
  2046.         }
  2047.         
  2048.         // fill in the default contents of the window
  2049.         pData->windowKind         = windowKind;
  2050.         pData->originalFileType    = fileType;
  2051.         pData->pMakeWindow         = (MakeWindowProc)thePreflight.makeProcPtr;
  2052.         pData->resRefNum        = -1;
  2053.         pData->dataRefNum        = -1;
  2054.         pData->contentRect         = GetWindowPort(pWindow)->portRect;
  2055.         
  2056.         // make the scroll bars
  2057.         {
  2058.         Rect    controlRect;
  2059.         
  2060.         if (thePreflight.wantHScroll)
  2061.             {
  2062.             pData->contentRect.bottom -= kScrollBarSize;
  2063.             controlRect = GetWindowPort(pWindow)->portRect;
  2064.             controlRect.top = controlRect.bottom - 16;
  2065.             if (thePreflight.wantVScroll)
  2066.                 controlRect.right -= kGrowScrollAdjust;
  2067.             OffsetRect(&controlRect, -1, 1);
  2068.             pData->hScroll = NewControl(pWindow, &controlRect, "\p", true, 0, 0, 0, scrollBarProc, 0);
  2069.             }
  2070.         if (thePreflight.wantVScroll)
  2071.             {
  2072.             pData->contentRect.right -= kScrollBarSize;
  2073.             controlRect = GetWindowPort(pWindow)->portRect;
  2074.             controlRect.left = controlRect.right - 16;
  2075.             if (thePreflight.wantVScroll)
  2076.                 controlRect.bottom -= kGrowScrollAdjust;
  2077.             OffsetRect(&controlRect, 1, -1);
  2078.             pData->vScroll = NewControl(pWindow, &controlRect, "\p", true, 0, 0, 0, scrollBarProc, 0);
  2079.             }
  2080.         }
  2081.  
  2082.         // got a name?  Open the file        
  2083.         if (fileSpec)
  2084.             {
  2085.             anErr = FSpOpenDF(fileSpec, thePreflight.openKind, &pData->dataRefNum);
  2086.             if     ( 
  2087.                     ((anErr == afpAccessDenied) || (anErr == opWrErr) || (anErr == permErr) ) && 
  2088.                     (thePreflight.openKind != fsRdPerm)
  2089.                 )
  2090.                 {
  2091.                 thePreflight.openKind = fsRdPerm;
  2092.                 pData->isWritable = false;
  2093.                 anErr = FSpOpenDF(fileSpec, thePreflight.openKind, &pData->dataRefNum);
  2094.                 }
  2095.             else
  2096.                 pData->isWritable = true;
  2097.             nrequire(anErr, FailedToOpenFile);
  2098.  
  2099.             // okay not to find a resource fork, because some don't have them                
  2100.             pData->resRefNum = FSpOpenResFile(fileSpec, thePreflight.openKind);
  2101.             
  2102.             // save the file spec in case someone is interested
  2103.             pData->fileSpec = *fileSpec;
  2104.             }
  2105.             
  2106.         if (pData->pMakeWindow)
  2107.             {
  2108.             Rect oldContent = pData->contentRect;
  2109.             anErr = (*(pData->pMakeWindow)) (pWindow, pData);
  2110.             if (!EqualRect(&oldContent, &pData->contentRect))
  2111.                 {
  2112.                 SizeWindow(pWindow, 
  2113.                         pData->contentRect.right  + (pData->vScroll != 0) * kScrollBarSize,
  2114.                         pData->contentRect.bottom + (pData->hScroll != 0) * kScrollBarSize,
  2115.                         false);
  2116.                 }
  2117.             }
  2118.         nrequire(anErr, FailedMakeWindow);
  2119.  
  2120.         // got a name?  Use it as the window title
  2121.         if ( (fileSpec) && (!pData->openAsNew) )
  2122.             SetWTitle(pWindow, fileSpec->name);
  2123.         else
  2124.             {
  2125.             if ((gMachineInfo.documentCount == 1) && (pData->windowKind == kTextWindow))
  2126.                 {
  2127.                 Str255 tempString;
  2128.         
  2129.                 GetIndString(tempString, kMiscStrings, iFirstNewDocumentTitle);    // get the "untitled" string (no number)
  2130.                 SetWTitle(pWindow, tempString);
  2131.                 }
  2132.             else
  2133.                 {
  2134.                 Str255    tempString;
  2135.                 Str32    numString;
  2136.     
  2137.                 GetWTitle(pWindow, tempString);
  2138.                 NumToString(gMachineInfo.documentCount, numString);
  2139.                 (void) ZeroStringSub(tempString, numString);
  2140.                 SetWTitle(pWindow, tempString);
  2141.                 }
  2142.  
  2143.             if (pData->bumpUntitledCount)
  2144.                 gMachineInfo.documentCount++;    // bump count if appropriate for this kind of document
  2145.             }
  2146.  
  2147.         // Make sure the scroll bars are reasonable in size, and move if they must
  2148.         AdjustScrollBars(pWindow, true, true, nil);
  2149.  
  2150.         // finally, if all goes well, we can see the window itself!
  2151.         ShowWindow(pWindow);
  2152.         }
  2153.  
  2154.     return noErr;
  2155.  
  2156. // EXCEPTION HANDLING
  2157. FailedMakeWindow:
  2158.     if (pData->resRefNum != -1)
  2159.         CloseResFile(pData->resRefNum);
  2160.     if (pData->dataRefNum != -1)
  2161.         FSClose(pData->dataRefNum);
  2162.     
  2163. FailedToOpenFile:
  2164.     CloseWindow(pWindow);
  2165.     
  2166. NewWindowFailed:
  2167.     DisposePtr((Ptr)pData);
  2168.     
  2169. FailedToAllocateWindow:
  2170. PreflightFailed:
  2171. SanityCheckFailed:
  2172.     return anErr;
  2173.     
  2174. } // MakeNewWindow
  2175.  
  2176. // --------------------------------------------------------------------------------------------------------------
  2177. #pragma segment Main
  2178.  
  2179. #define dontSaveChanges 3
  2180.  
  2181. #define kVisualDelay 8
  2182.  
  2183. static pascal Boolean SaveChangesFilter(DialogRef theDialog, EventRecord *theEvent, short *itemHit)
  2184. {
  2185.     if (StdFilterProc(theDialog, theEvent, itemHit))
  2186.         return true;
  2187.  
  2188.     if (theEvent->what == updateEvt)
  2189.         {
  2190.         HandleEvent(theEvent);
  2191.         }
  2192.  
  2193.     if (theEvent->what == keyDown)
  2194.         {
  2195.         StringPtr keyEquivs = *GetString(kSaveChangesWindowID);
  2196.         unsigned char theKey = theEvent->message & charCodeMask;
  2197.  
  2198.         if (keyEquivs && (theKey == keyEquivs[1] || theKey == keyEquivs[2]))
  2199.             {
  2200.             short itemType;
  2201.             Rect theRect;
  2202.             ControlRef theControl;
  2203.             long finalTicks;
  2204.  
  2205.             GetDialogItem(theDialog, dontSaveChanges, &itemType, (Handle *) &theControl, &theRect);
  2206.             HiliteControl(theControl, kControlButtonPart);
  2207.             Delay(kVisualDelay, &finalTicks);
  2208.             HiliteControl(theControl, 0);
  2209.  
  2210.             *itemHit = dontSaveChanges;
  2211.             return true;
  2212.             }
  2213.         }
  2214.  
  2215.     return false;
  2216. }
  2217.  
  2218.  
  2219. static OSErr DoCloseWindow(WindowRef pWindow)
  2220. {
  2221.     OSErr            anErr = noErr;
  2222.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2223.     
  2224.     if ( (pData) && (pData->changed) )
  2225.         {
  2226.         short         hit;
  2227.         Str255        wTitle;
  2228.         DialogRef    dPtr;
  2229.         
  2230.         GetWTitle(pWindow, wTitle);
  2231.         SetCursor(&qd.arrow);
  2232.         ParamText(wTitle, "\p", "\p", "\p");
  2233.         
  2234.         hit = cancel;
  2235.         dPtr = GetNewDialog(kSaveChangesWindowID, nil, (WindowRef)-1);
  2236.         if (dPtr)
  2237.             {
  2238.             SetDialogDefaultItem(dPtr, ok);
  2239.             SetDialogCancelItem (dPtr, cancel);
  2240.             BeginMovableModal();
  2241.             do
  2242.                 {
  2243.                 MovableModalDialog(SaveChangesFilter, &hit);
  2244.                 } while ((hit != dontSaveChanges) && (hit != ok) && (hit != cancel));
  2245.                 
  2246.             DisposeDialog(dPtr);
  2247.             EndMovableModal();
  2248.             }
  2249.         switch (hit)
  2250.             {
  2251.             case ok:
  2252.                 anErr = DoCommand(pWindow, cSave, 0);
  2253.                 if (anErr == eUserCanceled)        // redundant?
  2254.                     gAllDone = false;
  2255.                 break;
  2256.                 
  2257.             case cancel:
  2258.                 anErr = eUserCanceled;
  2259.                 gAllDone = false;
  2260.                 break;
  2261.                 
  2262.             case dontSaveChanges:
  2263.                 // don't save, so just close it
  2264.                 break;
  2265.             }
  2266.         }
  2267.  
  2268.     if (anErr == noErr)
  2269.         {
  2270.         if ( (pData) && (pData->pCloseWindow) )
  2271.             {
  2272.             // let the object close the window if it wishes to
  2273.             anErr = (*(pData->pCloseWindow)) (pWindow, pData);
  2274.             }
  2275.  
  2276.         // otherwise we close it the default way
  2277.         if (anErr == noErr)
  2278.             {
  2279.             if (pData)
  2280.                 {
  2281.                 CloseWindow(pWindow);
  2282.     
  2283.                 if (pData->hPrint)
  2284.                     {
  2285.                     if (gMachineInfo.haveGX)
  2286.                         GXDisposeJob( pData->hPrint);
  2287.                     else
  2288.                         DisposeHandle((Handle) pData->hPrint);
  2289.                     }
  2290.                     
  2291.                 if (pData->resRefNum != -1)
  2292.                     CloseResFile(pData->resRefNum);
  2293.                 if (pData->dataRefNum != -1)
  2294.                     FSClose(pData->dataRefNum);
  2295.                 DisposePtr((Ptr) pData);
  2296.                 }
  2297.             }
  2298.         }
  2299.  
  2300.     // If we closed the last window, clean up
  2301.     if (FrontWindow() == nil)
  2302.         {
  2303.         AdjustMenus(nil, true, false);
  2304.         gMachineInfo.documentCount = 1;        // back to "untitled"
  2305.         }
  2306.     
  2307.     return anErr;
  2308.     
  2309. } // DoCloseWindow
  2310.  
  2311. #undef dontSaveChanges
  2312.  
  2313. // --------------------------------------------------------------------------------------------------------------
  2314. #pragma segment Main
  2315.  
  2316. static OSErr    DetermineWindowTypeOrOpen(
  2317.     FSSpecPtr theSpec, OSType theType,                 // optional input params -- file to open
  2318.     OSType *returnedTypeList, short * pNumTypes,    // optional input params -- returns list of files
  2319.     Boolean *pWasAlreadyOpen)                        // optional input params -- was file already open
  2320. {
  2321.     OSErr        anErr = noErr;
  2322.     OSType        typeList[20];
  2323.     OSType        docList[20];
  2324.     short        numTypes;
  2325.  
  2326.     // use local copies if the input params are nil    
  2327.     if (returnedTypeList == nil)
  2328.         returnedTypeList = &typeList[0];
  2329.     if (pNumTypes == nil)
  2330.         pNumTypes = &numTypes;
  2331.     *pNumTypes = 0;
  2332.     
  2333.     // Load up all of the file types we know how to handle
  2334.     AboutGetFileTypes(returnedTypeList, docList, pNumTypes);
  2335.     PICTGetFileTypes(returnedTypeList, docList, pNumTypes);
  2336.     MovieGetFileTypes(returnedTypeList, docList, pNumTypes);
  2337.     ClipboardGetFileTypes(returnedTypeList, docList, pNumTypes);
  2338.     TextGetFileTypes(returnedTypeList, docList, pNumTypes);
  2339.     GXGetFileTypes(returnedTypeList, docList, pNumTypes);
  2340.     ThreeDGetFileTypes(returnedTypeList, docList, pNumTypes);
  2341.  
  2342.     if (theSpec != nil)
  2343.         {
  2344.         short         index;
  2345.         OSType        windowType = '????';
  2346.  
  2347.         for (index = 0; index < (*pNumTypes); ++index)
  2348.             if (theType == returnedTypeList[index])
  2349.                 windowType = docList[index];
  2350.                 
  2351.         if (windowType != '????')
  2352.             {
  2353.             
  2354.             if ( (theType == 'TEXT') || (theType == 'sEXT') )
  2355.                 {
  2356.                 FInfo    theInfo;
  2357.                 
  2358.                 FSpGetFInfo(theSpec, &theInfo);
  2359.                 if ((theInfo.fdFlags & kIsStationary) != 0)
  2360.                     theType = 'sEXT';
  2361.                 else
  2362.                     theType = 'TEXT';
  2363.                 }
  2364.                 
  2365.             anErr = MakeNewWindow(windowType, theSpec, theType, pWasAlreadyOpen);
  2366.             }
  2367.         else
  2368.             anErr = eDocumentWrongKind;
  2369.         }
  2370.         
  2371.         
  2372.     return anErr;
  2373.     
  2374. } // DetermineWindowTypeOrOpen
  2375.  
  2376. // --------------------------------------------------------------------------------------------------------------
  2377. #pragma segment Main
  2378.  
  2379. // Handle update/activate events behind Standard File
  2380. static pascal Boolean OpenDialogFilter(DialogRef theDialog, EventRecord *theEvent,
  2381.                                       short *itemHit, void *myDataPtr)
  2382. {
  2383.     #pragma unused(myDataPtr)
  2384.  
  2385.     // Pass updates through (Activates are tricky...was mucking with Apple menu & thereby
  2386.     // drastically changing how the system handles the menu bar during our alert)
  2387.     if ( (theEvent->what == updateEvt) && (theEvent->message != (long)theDialog) )
  2388.         HandleEvent(theEvent);
  2389.  
  2390.     if (StdFilterProc(theDialog, theEvent, itemHit))
  2391.         return(true);
  2392.  
  2393.     return(false);
  2394.  
  2395. } // OpenDialogFilter
  2396.  
  2397. #if GENERATINGCFM
  2398.     static RoutineDescriptor gOpenDialogFilterRD = BUILD_ROUTINE_DESCRIPTOR(uppModalFilterYDProcInfo, OpenDialogFilter);
  2399.     static ModalFilterYDUPP gOpenDialogFilter = &gOpenDialogFilterRD;
  2400. #else
  2401.     static ModalFilterYDUPP gOpenDialogFilter = NewModalFilterYDProc(OpenDialogFilter);
  2402. #endif
  2403.  
  2404.  
  2405. static OSErr DoOpenWindow(void)
  2406. {
  2407.     OSErr                anErr = noErr;
  2408.     short                numTypes;
  2409.     OSType                typeList[20];
  2410.     StandardFileReply    sfReply;
  2411.     Point thePoint = { -1, -1 };
  2412.  
  2413.     DetermineWindowTypeOrOpen(nil, '????', &typeList[0], &numTypes, nil);
  2414.     
  2415.     if (gMachineInfo.haveQuickTime)
  2416.         {
  2417.         CustomGetFilePreview(nil, numTypes, typeList, &sfReply, 0, thePoint, nil, gOpenDialogFilter, nil, nil, nil);
  2418.         }
  2419.     else
  2420.         {
  2421.         CustomGetFile(nil, numTypes, typeList, &sfReply, 0, thePoint, nil, gOpenDialogFilter, nil, nil, nil);
  2422.         }
  2423.     
  2424.     if (sfReply.sfGood)
  2425.         {
  2426.         SetWatchCursor();
  2427.         
  2428.         anErr = DetermineWindowTypeOrOpen(&sfReply.sfFile, sfReply.sfType, &typeList[0], &numTypes, nil);
  2429.  
  2430.         SetCursor(&qd.arrow);
  2431.         }
  2432.         
  2433.     return anErr;
  2434.     
  2435. } // DoOpenWindow
  2436.  
  2437. // --------------------------------------------------------------------------------------------------------------
  2438. #pragma segment Main
  2439.  
  2440. static OSErr DoUpdateWindow(WindowRef pWindow)
  2441. {
  2442.     OSErr            anErr = noErr;
  2443.     WindowDataPtr    pData = GetWindowInfo(pWindow);
  2444.     GrafPtr            curPort;
  2445.     
  2446.     // only handle updates for windows we know about
  2447.     if (pData)
  2448.         {
  2449.         GetPort(&curPort);
  2450.         SetPort((GrafPtr)GetWindowPort(pWindow));
  2451.         BeginUpdate(pWindow);
  2452.                     
  2453.         if (pData->pUpdateWindow)
  2454.             anErr = (*(pData->pUpdateWindow)) (pWindow, pData);
  2455.     
  2456.         EndUpdate(pWindow);
  2457.         SetPort(curPort);
  2458.         }
  2459.     
  2460.     return anErr;
  2461.     
  2462. } // DoUpdateWindow
  2463.  
  2464. // --------------------------------------------------------------------------------------------------------------
  2465. #pragma segment Main
  2466.  
  2467. OSErr DoScrollContent(WindowRef pWindow, WindowDataPtr pData, short deltaH, short deltaV)
  2468. {
  2469.     OSErr    anErr = noErr;
  2470.     
  2471.     if ((deltaH) || (deltaV))
  2472.         {
  2473.         // if we have a balloon displayed, update before scrolling anything
  2474.         if (HMIsBalloon())
  2475.             DoUpdateWindow(pWindow);
  2476.         
  2477.         if ((pData) && (pData->pScrollContent))
  2478.             anErr = (*(pData->pScrollContent)) (pWindow, pData, deltaH, deltaV);
  2479.             
  2480.         if (anErr == noErr)
  2481.             {
  2482.             RgnHandle    invalidRgn = NewRgn();
  2483.             
  2484.             ScrollRect(&pData->contentRect, deltaH, deltaV, invalidRgn);
  2485.             InvalRgn(invalidRgn);
  2486.             DisposeRgn(invalidRgn);
  2487.     
  2488.             DoUpdateWindow(pWindow);
  2489.             }
  2490.         }
  2491.     
  2492.     return anErr;
  2493.     
  2494. } // DoScrollContent
  2495.  
  2496. // --------------------------------------------------------------------------------------------------------------
  2497. // BEGIN SCROLL ACTION PROCS
  2498. // --------------------------------------------------------------------------------------------------------------
  2499. #pragma segment Main
  2500.  
  2501. void SetControlAndClipAmount(ControlRef control, short * amount)
  2502. {
  2503.     short        value, max;
  2504.     
  2505.     value = GetControlValue(control);    /* get current value */
  2506.     max = GetControlMaximum(control);        /* and maximum value */
  2507.     *amount = value - *amount;
  2508.     if ( *amount < 0 )
  2509.         *amount = 0;
  2510.     else
  2511.         {
  2512.         if ( *amount > max )
  2513.             *amount = max;
  2514.         }
  2515.     SetControlValue(control, *amount);
  2516.     *amount = value - *amount;        /* calculate the real change */
  2517.     
  2518. } // SetControlAndClipAmount
  2519.  
  2520. // --------------------------------------------------------------------------------------------------------------
  2521. static pascal void VActionProc(ControlRef control, short part)
  2522. {
  2523.     if (part != 0)
  2524.         {
  2525.         WindowRef        pWindow = (**control).contrlOwner;
  2526.         WindowDataPtr     pData = GetWindowInfo(pWindow);
  2527.         short            amount = 0;
  2528.         
  2529.         switch (part)
  2530.             {
  2531.             case kControlUpButtonPart:
  2532.                 amount = pData->vScrollAmount;
  2533.                 break;
  2534.                 
  2535.             case kControlDownButtonPart:
  2536.                 amount = -pData->vScrollAmount;
  2537.                 break;
  2538.                 
  2539.             // vertical page scrolling should be a multiple of the incremental scrolling -- so that
  2540.             // we avoid half-lines of text at the bottom of pages.
  2541.             
  2542.             // More generically, if there was a method for dealing with text scrolling by a non-constant
  2543.             // amount, this would be better -- but SimpleText currently doesn't have a framework to allow
  2544.             // the document object to override the scroll amount dynamically.  Maybe something to add in
  2545.             // the future.
  2546.             case kControlPageUpPart:
  2547.                 amount = (((pData->contentRect.bottom - pData->contentRect.top) / pData->vScrollAmount)-1) * pData->vScrollAmount;
  2548.                 if (amount == 0)
  2549.                     amount = pData->contentRect.bottom - pData->contentRect.top;
  2550.                 break;
  2551.  
  2552.             case kControlPageDownPart:
  2553.                 amount = (((pData->contentRect.top - pData->contentRect.bottom) / pData->vScrollAmount)+1) * pData->vScrollAmount;
  2554.                 if (amount == 0)
  2555.                     amount = pData->contentRect.top - pData->contentRect.bottom;
  2556.                 break;
  2557.             }
  2558.         
  2559.         SetControlAndClipAmount(control, &amount);
  2560.         if (amount != 0)
  2561.             DoScrollContent(pWindow, pData, 0, amount);
  2562.         }
  2563.         
  2564. } // VActionProc
  2565.  
  2566. #if GENERATINGCFM
  2567.     static RoutineDescriptor gVActionProcRD = BUILD_ROUTINE_DESCRIPTOR(uppControlActionProcInfo, VActionProc);
  2568.     static ControlActionUPP gVActionProc = &gVActionProcRD;
  2569. #else
  2570.     static ControlActionUPP gVActionProc = VActionProc;
  2571. #endif
  2572.  
  2573. // --------------------------------------------------------------------------------------------------------------
  2574. static pascal void HActionProc(ControlRef control, short part)
  2575. {
  2576.     if (part != 0)
  2577.         {
  2578.         WindowRef        pWindow = (**control).contrlOwner;
  2579.         WindowDataPtr     pData = GetWindowInfo(pWindow);
  2580.         short            amount = 0;
  2581.         
  2582.         switch (part)
  2583.             {
  2584.             case kControlUpButtonPart:
  2585.                 amount = pData->hScrollAmount;
  2586.                 break;
  2587.                 
  2588.             case kControlDownButtonPart:
  2589.                 amount = -pData->hScrollAmount;
  2590.                 break;
  2591.                 
  2592.             case kControlPageUpPart:
  2593.                 amount = pData->contentRect.right - pData->contentRect.left;
  2594.                 break;
  2595.  
  2596.             case kControlPageDownPart:
  2597.                 amount = pData->contentRect.left - pData->contentRect.right;
  2598.                 break;
  2599.             }
  2600.         
  2601.         SetControlAndClipAmount(control, &amount);
  2602.         if (amount != 0)
  2603.             DoScrollContent(pWindow, pData, amount, 0);
  2604.         }
  2605.         
  2606. } // HActionProc
  2607.  
  2608. #if GENERATINGCFM
  2609.     static RoutineDescriptor gHActionProcRD = BUILD_ROUTINE_DESCRIPTOR(uppControlActionProcInfo, HActionProc);
  2610.     static ControlActionUPP gHActionProc = &gHActionProcRD;
  2611. #else
  2612.     static ControlActionUPP gHActionProc = HActionProc;
  2613. #endif
  2614.  
  2615. // --------------------------------------------------------------------------------------------------------------
  2616. // END SCROLL ACTION PROCS
  2617. // --------------------------------------------------------------------------------------------------------------
  2618.  
  2619. #pragma segment Main
  2620.  
  2621. static OSErr DoContentClick(WindowRef pWindow)
  2622. {
  2623.     OSErr            anErr = noErr;
  2624.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2625.     
  2626.     
  2627.     if ( pData )
  2628.         {
  2629.         SetPort((GrafPtr) GetWindowPort(pWindow));
  2630.         
  2631.         if (pData->pContentClick)
  2632.             {
  2633.             // let the object handle the click if it wishes to
  2634.             anErr = (*(pData->pContentClick)) (pWindow, pData, &gEvent);
  2635.             }
  2636.         
  2637.         if (anErr == noErr) 
  2638.             {
  2639.             ControlRef        theControl;
  2640.             short            part;
  2641.             
  2642.             GlobalToLocal(&gEvent.where);
  2643.             part = FindControl(gEvent.where, pWindow, &theControl);
  2644.             switch (part)
  2645.                 {
  2646.                 // do nothing for viewRect case
  2647.                 case 0:
  2648.                     break;
  2649.  
  2650.                 // track the thumb, and then update all at once
  2651.                 case kControlIndicatorPart:
  2652.                     {
  2653.                     short    value = GetControlValue(theControl);
  2654.                     
  2655.                     part = TrackControl(theControl, gEvent.where, nil);
  2656.                     if (part != 0)
  2657.                         {
  2658.                         // turn the value into a delta
  2659.                         value -= GetControlValue(theControl);
  2660.                         
  2661.                         // if we actually moved
  2662.                         if (value != 0)
  2663.                             {
  2664.                             if (theControl == pData->hScroll)
  2665.                                 DoScrollContent(pWindow, pData, value, 0);
  2666.                             if (theControl == pData->vScroll)
  2667.                                 DoScrollContent(pWindow, pData, 0, value);
  2668.                                 
  2669.                             }
  2670.                         }
  2671.                     }
  2672.                     break;
  2673.                     
  2674.                 // track the control, and scroll as we go
  2675.                 default:
  2676.                     if (theControl)
  2677.                         {
  2678.                         if (theControl == pData->hScroll)
  2679.                             part = TrackControl(theControl, gEvent.where, gHActionProc);
  2680.                         if (theControl == pData->vScroll)
  2681.                             part = TrackControl(theControl, gEvent.where, gVActionProc);
  2682.                         }
  2683.                     break;
  2684.                 }
  2685.             }
  2686.  
  2687.         }
  2688.  
  2689.         
  2690.     return anErr;
  2691.     
  2692. } // DoContentClick
  2693.  
  2694. // --------------------------------------------------------------------------------------------------------------
  2695. #pragma segment Main
  2696.  
  2697. static OSErr DoGrowWindow(WindowRef pWindow, EventRecord *pEvent)
  2698. {
  2699.     OSErr            anErr = noErr;
  2700.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2701.     Rect            tempRect;
  2702.     LongRect        docRect;
  2703.     long            growResult;
  2704.     
  2705.     if (pData)
  2706.         {
  2707.         GrafPtr    pPort = (GrafPtr)GetWindowPort(pWindow);
  2708.         
  2709.         SetPort(pPort);
  2710.         
  2711.         RectToLongRect(&pData->contentRect, &docRect);
  2712.         if (pData->pGetDocumentRect)
  2713.             (*(pData->pGetDocumentRect)) (pWindow, pData, &docRect, true);
  2714.         if (pData->vScroll)
  2715.             docRect.right += 16;
  2716.         if (pData->hScroll)
  2717.             docRect.bottom += 16;
  2718.         
  2719.         if ( (pData->hasGrow) && (pData->hScroll == nil) && (pData->vScroll == nil) )
  2720.             {
  2721.             docRect.right += 16;
  2722.             docRect.bottom += 16;
  2723.             }
  2724.             
  2725.         // set up resize constraints
  2726.         tempRect.left = pData->minHSize;
  2727.         if (tempRect.left == 0)
  2728.             tempRect.left = kMinDocSize;
  2729.         tempRect.right = docRect.right - docRect.left;
  2730.         if (tempRect.right < tempRect.left)
  2731.             tempRect.right = tempRect.left;
  2732.         tempRect.top = pData->minVSize;
  2733.         if (tempRect.top == 0)
  2734.             tempRect.top = kMinDocSize;
  2735.         tempRect.bottom = docRect.bottom - docRect.top;
  2736.         if (tempRect.bottom < tempRect.top)
  2737.             tempRect.bottom = tempRect.top;
  2738.             
  2739.         growResult = GrowWindow(pWindow, pEvent->where, &tempRect);
  2740.         if ( growResult != 0 ) 
  2741.             {
  2742.             Rect        oldRect;
  2743.             RgnHandle    currentInval = NewRgn();
  2744.             Boolean        needInvalidate;
  2745.             
  2746.             // save old content area
  2747.             oldRect = pData->contentRect;
  2748.             
  2749.             // save old pending update
  2750.             GetWindowUpdateRgn(pWindow, currentInval);
  2751.             OffsetRgn(currentInval, pPort->portBits.bounds.left, pPort->portBits.bounds.top);
  2752.             
  2753.             // grow window and recalc what is needed
  2754.             SizeWindow(pWindow, growResult & 0xFFFF, growResult >> 16, true);
  2755.             AdjustScrollBars(pWindow, true, true, &needInvalidate);
  2756.             
  2757.             if (needInvalidate)
  2758.                 {
  2759.                 InvalRect(&pData->contentRect);
  2760.                 }
  2761.             else
  2762.                 {
  2763.                 // don't bother to redraw things that haven't changed
  2764.                 SectRect(&oldRect, &pData->contentRect, &oldRect);
  2765.                 ValidRect(&oldRect);
  2766.                 
  2767.                 // but, if a pending update was there, be sure to deal with that!
  2768.                 InvalRgn(currentInval);
  2769.                 }
  2770.  
  2771.             // if we have offset scrollbars, then force a redraw of them
  2772.             if (pData->hScrollOffset)
  2773.                 {
  2774.                 oldRect = GetWindowPort(pWindow)->portRect;
  2775.                 oldRect.right = oldRect.left + pData->hScrollOffset;
  2776.                 oldRect.top = oldRect.bottom - kScrollBarSize;
  2777.                 InvalRect(&oldRect);
  2778.                 }
  2779.             if (pData->vScrollOffset)
  2780.                 {
  2781.                 oldRect = GetWindowPort(pWindow)->portRect;
  2782.                 oldRect.bottom = oldRect.top + pData->vScrollOffset;
  2783.                 oldRect.left = oldRect.right - kScrollBarSize;
  2784.                 InvalRect(&oldRect);
  2785.                 }
  2786.  
  2787.             DisposeRgn(currentInval);
  2788.             }
  2789.             
  2790.         }
  2791.         
  2792.     
  2793.     return anErr;
  2794.     
  2795. } // DoGrowWindow
  2796.  
  2797. // --------------------------------------------------------------------------------------------------------------
  2798. #pragma segment Main
  2799.  
  2800. static OSErr DoZoomWindow(WindowRef pWindow, short zoomDir)
  2801. {
  2802.     Rect                *windRect, *zoomRect;
  2803.     Rect                globalPortRect, theSect, dGDRect;
  2804.     GDHandle            nthDevice, dominantGDevice;
  2805.     long                sectArea, greatestArea;
  2806.     short                 hMax, vMax;
  2807.     
  2808.     // determine the max size of the window
  2809.     {
  2810.     WindowDataPtr        pData = GetWindowInfo(pWindow);
  2811.     LongRect            docRect;
  2812.     
  2813.     RectToLongRect(&pData->contentRect, &docRect);
  2814.     if (pData->pGetDocumentRect)
  2815.         (*(pData->pGetDocumentRect)) (pWindow, pData, &docRect, true);
  2816.     if (pData->vScroll)
  2817.         docRect.right += kScrollBarSize;
  2818.     if (pData->hScroll)
  2819.         docRect.bottom += kScrollBarSize;
  2820.     
  2821.     if ( (pData->hasGrow) && (pData->hScroll == nil) && (pData->vScroll == nil) )
  2822.         {
  2823.         docRect.right += kScrollBarSize;
  2824.         docRect.bottom += kScrollBarSize;
  2825.         }
  2826.  
  2827.     hMax = docRect.right - docRect.left;
  2828.     vMax = docRect.bottom - docRect.top;
  2829.     }
  2830.     
  2831.     SetPort((GrafPtr) GetWindowPort(pWindow));
  2832.     EraseRect(&GetWindowPort(pWindow)->portRect);    // recommended for cosmetic reasons
  2833.  
  2834.     if (zoomDir == inZoomOut) 
  2835.         {
  2836.  
  2837.         /*
  2838.          *    ZoomWindow() is a good basic tool, but it doesn't do everything necessary to
  2839.          *    implement a good human interface when zooming. In fact it's not even close for
  2840.          *    more high-end hardware configurations. We must help it along by calculating an
  2841.          *    appropriate window size and location any time a window zooms out.
  2842.          */
  2843.         {
  2844.         RgnHandle    structRgn = NewRgn();
  2845.         
  2846.         GetWindowStructureRgn(pWindow, structRgn);
  2847.         windRect = &(**structRgn).rgnBBox;
  2848.         DisposeRgn(structRgn);
  2849.         }
  2850.         dominantGDevice = nil;
  2851.         if (gMachineInfo.theEnvirons.hasColorQD) 
  2852.             {
  2853.  
  2854.             /*
  2855.              *    Color QuickDraw implies the possibility of multiple monitors. This is where
  2856.              *    zooming becomes more interesting. One should zoom onto the monitor containing
  2857.              *    the greatest portion of the window. This requires walking the gDevice list.
  2858.              */
  2859.  
  2860.             nthDevice = GetDeviceList();
  2861.             greatestArea = 0;
  2862.             while (nthDevice != nil) 
  2863.                 {
  2864.                 if (TestDeviceAttribute(nthDevice, screenDevice)) 
  2865.                     {
  2866.                     if (TestDeviceAttribute(nthDevice, screenActive)) 
  2867.                         {
  2868.                         SectRect(windRect, &(**nthDevice).gdRect, &theSect);
  2869.                         sectArea = (long) RectWidth(theSect) * (long) RectHeight(theSect);
  2870.                         if (sectArea > greatestArea) 
  2871.                             {
  2872.                             greatestArea = sectArea;        // save the greatest intersection
  2873.                             dominantGDevice = nthDevice;    // and which device it belongs to
  2874.                             }
  2875.                         }
  2876.                     }
  2877.                 nthDevice = GetNextDevice(nthDevice);
  2878.                 }
  2879.             }
  2880.  
  2881.         /*
  2882.          *    At this point, we know the dimensions of the window we're zooming, and we know
  2883.          *    what screen we're going to put it on. To be more specific, however, we need a
  2884.          *    rectangle which defines the maximum dimensions of the resized window's contents.
  2885.          *    This rectangle accounts for the thickness of the window frame, the menu bar, and
  2886.          *    one or two pixels around the edges for cosmetic compatibility with ZoomWindow().
  2887.          */
  2888.  
  2889.         if (dominantGDevice != nil) 
  2890.             {
  2891.             dGDRect = (**dominantGDevice).gdRect;
  2892.             if (dominantGDevice == GetMainDevice())        // account for menu bar on main device
  2893.                 dGDRect.top += GetMBarHeight();
  2894.             }
  2895.         else 
  2896.             {
  2897.             dGDRect = qd.screenBits.bounds;                // if no gDevice, use default monitor
  2898.             dGDRect.top += GetMBarHeight();
  2899.             }
  2900.  
  2901.         globalPortRect = GetWindowPort(pWindow)->portRect;
  2902.         LocalToGlobal(&TopLeft(globalPortRect));        // calculate the window's portRect
  2903.         LocalToGlobal(&BotRight(globalPortRect));        // in global coordinates
  2904.  
  2905.         // account for the window frame and inset it a few pixels
  2906.         dGDRect.left    += 2 + globalPortRect.left - windRect->left;
  2907.         dGDRect.top        += 2 + globalPortRect.top - windRect->top;
  2908.         dGDRect.right    -= 1 + windRect->right - globalPortRect.right;
  2909.         dGDRect.bottom    -= 1 + windRect->bottom - globalPortRect.bottom;
  2910.  
  2911.         /*
  2912.          *    Now we know exactly what our limits are, and since there are input parameters
  2913.          *    specifying the dimensions we'd like to see, we can move and resize the zoom
  2914.          *    state rectangle for the best possible results. We have three goals in this:
  2915.          *    1. Display the window entirely visible on a single device.
  2916.          *    2. Resize the window to best represent the dimensions of the document itself.
  2917.          *    3. Move the window as short a distance as possible to achieve #1 and #2.
  2918.          */
  2919.  
  2920.         zoomRect = &(**(WStateDataHandle) ((WindowPeek) pWindow)->dataHandle).stdState;
  2921.  
  2922.         /*
  2923.          *    Initially set the zoom rectangle to the size requested by the input parameters,
  2924.          *    although not smaller than a minimum size. We do this without moving the origin.
  2925.          */
  2926.  
  2927.         zoomRect->right = (zoomRect->left = globalPortRect.left) +
  2928.                                 Max(hMax, kMinDocSize);
  2929.         zoomRect->bottom = (zoomRect->top = globalPortRect.top) +
  2930.                                 Max(vMax, kMinDocSize);
  2931.  
  2932.         // Shift the entire rectangle if necessary to bring its origin inside dGDRect.
  2933.         OffsetRect(zoomRect,
  2934.                     Max(dGDRect.left - zoomRect->left, 0),
  2935.                     Max(dGDRect.top - zoomRect->top, 0));
  2936.  
  2937.         /*
  2938.          *    Shift the rectangle up and/or to the left if necessary to accomodate the view,
  2939.          *    and if it is possible to do so. The rectangle may not be moved such that its
  2940.          *    origin would fall outside of dGDRect.
  2941.          */
  2942.  
  2943.         OffsetRect(zoomRect,
  2944.                     -Pin(zoomRect->right - dGDRect.right, 0, zoomRect->left - dGDRect.left),
  2945.                     -Pin(zoomRect->bottom - dGDRect.bottom, 0, zoomRect->top - dGDRect.top));
  2946.  
  2947.         // Clip expansion to dGDRect, in case view is larger than dGDRect.
  2948.         zoomRect->right = Min(zoomRect->right, dGDRect.right);
  2949.         zoomRect->bottom = Min(zoomRect->bottom, dGDRect.bottom);
  2950.         }
  2951.  
  2952.     ZoomWindow(pWindow, zoomDir, pWindow == FrontWindow());
  2953.     
  2954.     AdjustScrollBars(pWindow, true, true, nil);
  2955.  
  2956.     InvalRect(&GetWindowPort(pWindow)->portRect);
  2957.     
  2958.     return noErr;
  2959.     
  2960. } // DoZoomWindow
  2961.  
  2962. // --------------------------------------------------------------------------------------------------------------
  2963. #pragma segment Main
  2964.  
  2965. OSErr DoActivate(WindowRef pWindow, Boolean activating)
  2966. {
  2967.  
  2968.     OSErr            anErr = noErr;
  2969.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2970.     
  2971.     SetPort((GrafPtr) GetWindowPort(pWindow));
  2972.     
  2973.     if ( pData )
  2974.         {
  2975.         if (pData->pActivateEvent)
  2976.             anErr = (*(pData->pActivateEvent)) (pWindow, pData, activating);
  2977.             
  2978.         if (anErr == noErr)
  2979.             {
  2980.             if (activating)
  2981.                 {
  2982.                 // Reshow all controls on activation
  2983.                 if (pData->hScroll)
  2984.                     ShowControl(pData->hScroll);
  2985.                 if (pData->vScroll)
  2986.                     ShowControl(pData->vScroll);
  2987.                 }
  2988.             else
  2989.                 {
  2990.                 // Hide all controls on deactivation
  2991.                 if (pData->hScroll)
  2992.                     HideControl(pData->hScroll);
  2993.                 if (pData->vScroll)
  2994.                     HideControl(pData->vScroll);
  2995.                 }
  2996.                 
  2997.             if (pData->hasGrow) 
  2998.                 {
  2999.                 Rect    growIconRect;
  3000.                 
  3001.                 CalculateGrowIcon(pData, &growIconRect);
  3002.                 InvalRect(&growIconRect);
  3003.                 }
  3004.             }
  3005.         }
  3006.  
  3007.     AdjustMenus(pWindow, true, false);
  3008.         
  3009.     return anErr;
  3010.     
  3011. } // DoActivate
  3012.  
  3013. // --------------------------------------------------------------------------------------------------------------
  3014. #pragma segment Main
  3015.  
  3016. static OSErr    DoStartupGX(void)
  3017. {
  3018.     gxGraphicsError    anErr = noErr;
  3019.     
  3020.     if (!gMachineInfo.haveStartedGX)
  3021.         {
  3022.         GXEnterGraphics();
  3023.         GXGetGraphicsError(&anErr);
  3024.         if ( (anErr == noErr) && (GXGetGraphicsClient() == nil) )
  3025.             anErr = out_of_memory;
  3026.         if (anErr == noErr)
  3027.             {
  3028.             anErr = GXInitPrinting();
  3029.             if (anErr != noErr)
  3030.                 GXExitGraphics();
  3031.             }
  3032.             
  3033.         if (anErr != noErr)
  3034.             GXSetGraphicsClient(nil);
  3035.         }
  3036.         
  3037.     if (GXGetGraphicsClient() == nil)
  3038.         anErr = out_of_memory;
  3039.  
  3040.     if (anErr == noErr)
  3041.         gMachineInfo.haveStartedGX = true;
  3042.  
  3043.     return anErr;
  3044.     
  3045. } // DoStartupGX
  3046.  
  3047. // --------------------------------------------------------------------------------------------------------------
  3048. #pragma segment Main
  3049.  
  3050. OSErr    DoDefault(WindowDataPtr     pData)
  3051. {
  3052.     OSErr    anErr = noErr;
  3053.     
  3054.     if (gMachineInfo.haveGX)
  3055.         {
  3056.         // start up GX, if needed
  3057.         anErr = DoStartupGX();
  3058.  
  3059.         if (anErr == noErr)
  3060.             {
  3061.             
  3062.             // default the job if we don't have it
  3063.             if (pData->hPrint == nil)
  3064.                 {
  3065.                 anErr = GXNewJob((gxJob*)&pData->hPrint);
  3066.                 if (anErr == noErr)
  3067.                     {
  3068.                     GXInstallApplicationOverride(pData->hPrint, gxPrintingEventMsg,         gPrintingEventMessage);
  3069.                     GXInstallApplicationOverride(pData->hPrint, gxCompleteSpoolFileMsg,     gCompleteSpoolFileMessage);
  3070.                     }
  3071.                 }
  3072.             }
  3073.         }
  3074.     else
  3075.         {
  3076.         PrOpen();
  3077.         anErr = PrError();
  3078.         if (anErr == noErr)
  3079.             {
  3080.             if (pData->hPrint == nil)
  3081.                 {
  3082.                 pData->hPrint = NewHandleClear(sizeof(TPrint));
  3083.                 anErr = MemError();
  3084.                 if (anErr == noErr)
  3085.                     PrintDefault(pData->hPrint);
  3086.                 }
  3087.                 
  3088.             }
  3089.         PrClose();
  3090.         }
  3091.         
  3092.     return anErr;
  3093.     
  3094. } // DoDefault
  3095.  
  3096. // --------------------------------------------------------------------------------------------------------------
  3097. #pragma segment Main
  3098.  
  3099. static void SetupForPrintDialogs(gxEditMenuRecord * pEdit)
  3100. {
  3101.     MenuHandle    menu;
  3102.     short        menuID, itemID;
  3103.  
  3104.     CommandToIDs(cCut,   &pEdit->editMenuID, &pEdit->cutItem);
  3105.     CommandToIDs(cCopy,  &pEdit->editMenuID, &pEdit->copyItem);
  3106.     CommandToIDs(cPaste, &pEdit->editMenuID, &pEdit->pasteItem);
  3107.     CommandToIDs(cClear, &pEdit->editMenuID, &pEdit->clearItem);
  3108.     CommandToIDs(cUndo,  &pEdit->editMenuID, &pEdit->undoItem);
  3109.     
  3110.     // diable everything we don't want to deal with
  3111.     for (menuID = mApple; menuID <= mLastMenu; ++menuID)
  3112.         {
  3113.         menu = GetMenuHandle(menuID);
  3114.         
  3115.         if (menu)
  3116.             {
  3117.             switch (menuID)
  3118.                 {
  3119.                 case mApple:
  3120.                     CommandToIDs(cAbout, &menuID, &itemID);
  3121.                     DisableItem(menu, itemID);
  3122.                     break;
  3123.                     
  3124.                 case mEdit:
  3125.                     CommandToIDs(cSelectAll, &menuID, &itemID);
  3126.                     DisableItem(menu, itemID);
  3127.                     CommandToIDs(cShowClipboard, &menuID, &itemID);
  3128.                     DisableItem(menu, itemID);
  3129.                     break;
  3130.                     
  3131.                 default:
  3132.                     DisableItem(menu, 0);
  3133.                     break;
  3134.                 }
  3135.             }
  3136.         }
  3137.     
  3138.     // Disable the current indicator because the dialogs are moveable modal    
  3139.     HiliteMenu(0);
  3140.         
  3141. } // SetupForPrintDialogs
  3142.  
  3143. // --------------------------------------------------------------------------------------------------------------
  3144. #pragma segment Main
  3145.  
  3146. OSErr    DoPageSetup(WindowRef pWindow)
  3147. {
  3148.     OSErr            anErr = noErr;
  3149.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  3150.         
  3151.     anErr = DoDefault(pData);
  3152.     nrequire(anErr, DoDefault);
  3153.     
  3154.     if (gMachineInfo.haveGX)
  3155.         {
  3156.         gxEditMenuRecord    theEdit;
  3157.         
  3158.         SetupForPrintDialogs(&theEdit);
  3159.         
  3160.         GXJobDefaultFormatDialog(pData->hPrint, &theEdit);
  3161.         anErr = GXGetJobError(pData->hPrint);
  3162.             
  3163.         AdjustMenus(pWindow, true, true);
  3164.         }
  3165.     else
  3166.         {
  3167.         PrOpen();
  3168.         anErr = PrError();
  3169.         if (anErr == noErr)
  3170.             {
  3171.             SetCursor(&qd.arrow);
  3172.             PrStlDialog(pData->hPrint);
  3173.             }
  3174.         PrClose();
  3175.         }
  3176.  
  3177. // FALL THROUGH EXCEPTION HANDLING
  3178. DoDefault:        
  3179.     return anErr;
  3180.     
  3181. } // DoPageSetup
  3182.  
  3183. // --------------------------------------------------------------------------------------------------------------
  3184. #pragma segment Main
  3185.  
  3186. static OSErr    DoPrintSetup(WindowRef pWindow, StringPtr pPrinterName)
  3187. {
  3188.     OSErr            anErr = noErr;
  3189.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  3190.         
  3191.     anErr = DoDefault(pData);
  3192.     nrequire(anErr, DoDefault);
  3193.     
  3194.     if (gMachineInfo.haveGX)
  3195.         {
  3196.         gxEditMenuRecord    theEdit;
  3197.         gxDialogResult        result;
  3198.         
  3199.         // toss any previous errors that might be around
  3200.         (void)GXGetJobError(pData->hPrint);
  3201.         
  3202.         if ( (pPrinterName) && (pPrinterName[0] != 0) )
  3203.             GXSelectJobOutputPrinter(pData->hPrint, pPrinterName);
  3204.         else
  3205.             {
  3206.             SetupForPrintDialogs(&theEdit);
  3207.             result = GXJobPrintDialog(pData->hPrint, &theEdit);
  3208.             AdjustMenus(pWindow, true, true);
  3209.             }
  3210.         if (anErr == noErr)
  3211.             {
  3212.             anErr = GXGetJobError(pData->hPrint);
  3213.             if ( (anErr == noErr) && (result == gxCancelSelected) )
  3214.                 anErr = eUserCanceled;
  3215.             }
  3216.         }
  3217.     else
  3218.         {
  3219.         PrOpen();
  3220.         anErr = PrError();
  3221.         if (anErr == noErr)
  3222.             {
  3223.             SetCursor(&qd.arrow);
  3224.             if (PrJobDialog(pData->hPrint) == false)
  3225.                 anErr = eUserCanceled;
  3226.             }
  3227.             
  3228.         PrClose();
  3229.         }
  3230.         
  3231. // FALL THROUGH EXCEPTION HANDLING
  3232. DoDefault:        
  3233.     return anErr;
  3234.     
  3235. } // DoPrintSetup
  3236.  
  3237. // --------------------------------------------------------------------------------------------------------------
  3238. #pragma segment Main
  3239.  
  3240. static OSErr    DoPrint(WindowRef pWindow, void * hPrint, Boolean oneCopy)
  3241. {
  3242.     gxGraphicsError        anErr = noErr;
  3243.     WindowDataPtr         pData = GetWindowInfo(pWindow);
  3244.     Boolean                didAllocate = false;
  3245.     
  3246.     // use a watch cursor while printing
  3247.     SetWatchCursor();
  3248.     
  3249.     if (gMachineInfo.haveGX)
  3250.         {
  3251.         // startup GX, if needed
  3252.         anErr = DoStartupGX();
  3253.  
  3254.         if ( (anErr == noErr) && (hPrint == nil) )
  3255.             {
  3256.             anErr = GXNewJob((gxJob*)&hPrint);
  3257.             if (anErr == noErr)            
  3258.                 {
  3259.                 GXInstallApplicationOverride(hPrint, gxPrintingEventMsg,         gPrintingEventMessage);
  3260.                 GXInstallApplicationOverride(hPrint, gxCompleteSpoolFileMsg,     gCompleteSpoolFileMessage);
  3261.                 didAllocate = true;
  3262.                 }
  3263.             }
  3264.  
  3265.         if (anErr == noErr)
  3266.             {            
  3267.             Str255    docName;
  3268.             
  3269.             if (oneCopy)
  3270.                 {
  3271.                 gxCopiesInfo theCopies;
  3272.                 
  3273.                 theCopies.copies = 1;
  3274.                 AddCollectionItem(GXGetJobCollection(hPrint), gxCopiesTag, gxPrintingTagID, sizeof(theCopies), &theCopies);
  3275.                 }
  3276.                 
  3277.             GXSetJobRefCon(hPrint, pData);
  3278.             
  3279.             GetWTitle(pWindow, docName);
  3280.             GXStartJob(hPrint, docName, 0);
  3281.             anErr = GXGetJobError(hPrint);
  3282.             if (anErr == noErr)
  3283.                 {
  3284.                 long        first, last, pageIndex;
  3285.                 Rect        pageRect;
  3286.                 CGrafPort    thePort;
  3287.                 
  3288.                 // determine size of page, and number of pages
  3289.                 {
  3290.                 gxRectangle    pageSize;
  3291.                 GXGetFormatDimensions(GXGetJobFormat(hPrint, 1), &pageSize, nil);
  3292.                 pageRect.top    = pageSize.top >> 16;
  3293.                 pageRect.left    = pageSize.left >> 16;
  3294.                 pageRect.bottom    = pageSize.bottom >> 16;
  3295.                 pageRect.right    = pageSize.right >> 16;
  3296.                 }            
  3297.                 GXGetJobPageRange(hPrint, &first, &last);
  3298.                 anErr = GXGetJobError(hPrint);
  3299.                 if (first < 1)
  3300.                     first = 1;
  3301.                 if (last < first)
  3302.                     last = first;
  3303.                     
  3304.                 if (anErr == noErr)
  3305.                     {
  3306.                     // make a port to perform translation in
  3307.                     OpenCPort(&thePort);
  3308.                     
  3309.                     for (pageIndex = first; pageIndex <= last; ++pageIndex)
  3310.                         {
  3311.                         SetPort((GrafPtr) &thePort);
  3312.                         
  3313.                         if (pData->documentOutputsGX)
  3314.                             anErr = (*(pData->pPrintPage)) (pWindow, pData, &pageRect, &pageIndex);
  3315.                         else
  3316.                             {
  3317.                             Point                    patStretch = {1,1};
  3318.                             gxTranslationOption     options = gxOptimizedTranslation;
  3319.                             CatchRefCon                theRefCon;
  3320.                             
  3321.                             if (GXGetPrinterDriverType(GXGetJobPrinter(hPrint)) == 'post')
  3322.                                 options += gxPostScriptTargetTranslation;
  3323.                             else
  3324.                                 options += gxRasterTargetTranslation;
  3325.                             
  3326.                             theRefCon.theJob                    = hPrint;
  3327.                             theRefCon.doLayout                    = (pData->originalFileType == 'TEXT');
  3328.                             theRefCon.thePage                     = GXNewShape(gxPictureType);
  3329.                             theRefCon.thePageRectangle.top         = ff(pageRect.top);
  3330.                             theRefCon.thePageRectangle.left     = ff(pageRect.left);
  3331.                             theRefCon.thePageRectangle.bottom     = ff(pageRect.bottom);
  3332.                             theRefCon.thePageRectangle.right     = ff(pageRect.right);
  3333.     
  3334.                             GXInstallQDTranslator(
  3335.                                         qd.thePort,
  3336.                                         options,
  3337.                                         &pageRect,
  3338.                                         &pageRect,
  3339.                                         patStretch,
  3340.                                         gSimpleCatchShape,
  3341.                                         &theRefCon);
  3342.                             GXGetGraphicsError(&anErr);
  3343.                             
  3344.                             if (anErr == noErr)
  3345.                                 {
  3346.                                 long    whichPage = pageIndex;
  3347.                                 
  3348.                                 anErr = (*(pData->pPrintPage)) (pWindow, pData, &pageRect, &whichPage);
  3349.                                 GXRemoveQDTranslator(qd.thePort, nil);
  3350.                                 
  3351.                                 GXPrintPage(hPrint, pageIndex, nil, theRefCon.thePage);
  3352.                                 anErr = GXGetJobError(hPrint);
  3353.                                 pageIndex = whichPage;
  3354.                                 }
  3355.                             
  3356.                             GXDisposeShape(theRefCon.thePage);
  3357.                             }
  3358.                         
  3359.                         if (anErr == noErr)
  3360.                             GXGetGraphicsError(&anErr);
  3361.                         
  3362.                         // bail when we are told to stop
  3363.                         if ( (pageIndex == -1) || (anErr != noErr) )
  3364.                             break;
  3365.                         }
  3366.                     
  3367.                     // all done with our temp port and job
  3368.                     CloseCPort(&thePort);
  3369.                     }
  3370.                     
  3371.                 GXFinishJob(hPrint);
  3372.                 if (anErr == noErr) anErr = GXGetJobError(hPrint);
  3373.                 }
  3374.                 
  3375.             }
  3376.                 
  3377.         if (didAllocate)
  3378.             GXDisposeJob(hPrint);
  3379.  
  3380.         // restore those menus!
  3381.         AdjustMenus(pWindow, true, true);
  3382.  
  3383.         }
  3384.     else
  3385.         {
  3386.         TPPrPort    printingPort;
  3387.         
  3388.         PrOpen();
  3389.         anErr = PrError();
  3390.         if (anErr == noErr)
  3391.             {
  3392.             if (hPrint == nil)
  3393.                 {
  3394.                 hPrint = NewHandleClear(sizeof(TPrint));
  3395.                 anErr = MemError();
  3396.                 if (anErr == noErr)
  3397.                     {
  3398.                     PrintDefault(hPrint);
  3399.                     didAllocate = true;
  3400.                     }
  3401.                 }
  3402.             
  3403.             if (anErr == noErr)
  3404.                 {
  3405.                 short    firstPage, lastPage;
  3406.                 
  3407.                 // be sure to get the page range BEFORE calling PrValidate(), 
  3408.                 // which blows it away for many drivers.
  3409.                 firstPage = (**(THPrint)hPrint).prJob.iFstPage;
  3410.                 lastPage = (**(THPrint)hPrint).prJob.iLstPage;
  3411.                 
  3412.                 // make sure the print record is cool to use
  3413.                 PrValidate(hPrint);
  3414.  
  3415.                 // then clear out the job itself -- some drivers don't
  3416.                 // do this from within PrValidate().  We want the job
  3417.                 // clear in case the driver bases background behavior
  3418.                 // from this range (and many do).
  3419.                 (**(THPrint)hPrint).prJob.iFstPage = 1;
  3420.                 (**(THPrint)hPrint).prJob.iLstPage = 9999;
  3421.                 
  3422.                 if (oneCopy)
  3423.                     (** ((THPrint)hPrint)).prJob.iCopies = 1;
  3424.                     
  3425.                 // start printing, and then loop over the pages
  3426.                 printingPort = PrOpenDoc(hPrint, nil, nil);
  3427.                 anErr = PrError();
  3428.                 if (anErr == noErr)
  3429.                     {
  3430.                     long    pageIndex;
  3431.                     Rect    pageRect;
  3432.                     
  3433.                     SetPort((GrafPtr) printingPort);
  3434.                     
  3435.                     pageRect = (**(THPrint)hPrint).prInfo.rPage;
  3436.                     if (firstPage < 1)
  3437.                         firstPage = 1;
  3438.                     if (lastPage < firstPage)
  3439.                         lastPage = firstPage;
  3440.                     for (pageIndex = firstPage; pageIndex <= lastPage; ++pageIndex)
  3441.                         {
  3442.                         PrOpenPage(printingPort, nil);
  3443.                         anErr = PrError();
  3444.                         if (anErr == noErr)
  3445.                             anErr = (*(pData->pPrintPage)) (pWindow, pData, &pageRect, &pageIndex);
  3446.                             
  3447.                         PrClosePage(printingPort);
  3448.                         if (anErr == noErr)
  3449.                             anErr = PrError();
  3450.                         if ( (anErr != noErr) || (pageIndex == -1) )
  3451.                             break;
  3452.                         }
  3453.                     }
  3454.                     
  3455.                 // Finish up printing of the document
  3456.                 PrCloseDoc(printingPort);
  3457.                 if (anErr == noErr)
  3458.                     anErr = PrError();
  3459.                 if ( (anErr == noErr) && ((**(THPrint)hPrint).prJob.bJDocLoop == bSpoolLoop) )
  3460.                     {
  3461.                     TPrStatus    theStatus;
  3462.                     
  3463.                     PrPicFile(hPrint, nil, nil, nil, &theStatus);
  3464.                     anErr = PrError();
  3465.                     }
  3466.                 }
  3467.                 
  3468.             if (didAllocate)
  3469.                 DisposeHandle((Handle) hPrint);
  3470.                 
  3471.             }
  3472.         PrClose();
  3473.         }
  3474.     
  3475.         
  3476.     // restore cursor
  3477.     SetCursor(&qd.arrow);
  3478.  
  3479.     return anErr;
  3480.     
  3481. } // DoPrint
  3482.  
  3483. // --------------------------------------------------------------------------------------------------------------
  3484. #pragma segment Main
  3485.  
  3486. OSErr    DoCommand(WindowRef pWindow, short commandID, long menuResult)
  3487. {
  3488.     OSErr            anErr = noErr;
  3489.     WindowDataPtr     pData = nil;
  3490.     
  3491.     if (pWindow)
  3492.         {
  3493.         pData = (WindowDataPtr) GetWindowInfo(pWindow);
  3494.         
  3495.         if ( (pData) && (pData->pCommand) )
  3496.             anErr = (*(pData->pCommand)) (pWindow, pData, commandID, menuResult);
  3497.         }
  3498.     
  3499.     if (anErr == noErr)
  3500.         {
  3501.         // default command handling
  3502.         switch (commandID)
  3503.             {
  3504.             // About box command
  3505.             case cAbout:
  3506.                 if (!BringToFrontIfExists(kAboutWindow))
  3507.                     anErr = MakeNewWindow(kAboutWindow, nil, '????', nil);
  3508.                 break;
  3509.                 
  3510.             case cDeskAccessory:
  3511.                 {
  3512.                 Str255    tempString;
  3513.                 
  3514.                 GetMenuItemText(GetMenuHandle(menuResult>>16), menuResult & 0xFFFF, tempString);
  3515.                 OpenDeskAcc(tempString);
  3516.                 }
  3517.                 break;
  3518.                 
  3519.             // New window command
  3520.             case cNew:
  3521.                 anErr = MakeNewWindow(kTextWindow, nil, 'TEXT', nil);
  3522.                 break;
  3523.                 
  3524.             // Open window command
  3525.             case cOpen:
  3526.                 anErr = DoOpenWindow();
  3527.                 break;
  3528.                 
  3529.             // Close window command
  3530.             case cClose:
  3531.                 anErr = DoCloseWindow(pWindow);
  3532.                 break;
  3533.                 
  3534.             case cPageSetup:
  3535.                 anErr = DoPageSetup(pWindow);
  3536.                 break;
  3537.                 
  3538.             case cPrint:
  3539.                 anErr = DoPrintSetup(pWindow, nil);
  3540.                 if (anErr == noErr)
  3541.                     anErr = DoPrint(pWindow, pData->hPrint, false);
  3542.                 break;
  3543.                 
  3544.             case cPrintOneCopy:
  3545.                 anErr = DoPrint(pWindow, pData->hPrint, true);
  3546.                 break;
  3547.                 
  3548.             // get out of here command!
  3549.             case cQuit:
  3550.                 gAllDone = true;
  3551.                 break;
  3552.     
  3553.             // show/hide clipboard
  3554.             case cShowClipboard:
  3555.                 if (!BringToFrontIfExists(kClipboardWindow))
  3556.                     {
  3557.                     anErr = MakeNewWindow(kClipboardWindow, nil, '????', nil);
  3558.                     }
  3559.                 else
  3560.                     {
  3561.                     pWindow = FrontWindow();
  3562.                     anErr = DoCloseWindow(pWindow);
  3563.                     }
  3564.                 break;
  3565.                 
  3566.             case cNextPage:
  3567.                 gEvent.what = keyDown;
  3568.                 gEvent.message = kPageDown << 8;
  3569.                 gEvent.modifiers = 0;
  3570.                 DoKeyEvent(pWindow, &gEvent, false);
  3571.                 break;
  3572.                 
  3573.             case cPreviousPage:
  3574.                 gEvent.what = keyDown;
  3575.                 gEvent.message = kPageUp << 8;
  3576.                 gEvent.modifiers = 0;
  3577.                 DoKeyEvent(pWindow, &gEvent, false);
  3578.                 break;
  3579.                 
  3580.             // Do nothing command
  3581.             case cNull:
  3582.                 break;
  3583.                             
  3584.             default:
  3585.                 break;
  3586.             }
  3587.         }
  3588.         
  3589.     // don't report cancels
  3590.     if ( (anErr == iPrAbort) || (anErr == gxPrUserAbortErr) )
  3591.         anErr = noErr;
  3592.     
  3593.     // some errors map to other errors because they are basically the same
  3594.     // This way we can use the same string.
  3595.     if (anErr == out_of_memory)
  3596.         anErr = memFullErr;
  3597.         
  3598.     if ( (anErr != noErr) && (anErr != eActionAlreadyHandled) && (anErr != eUserCanceled) )
  3599.         {
  3600.         // some commands are so similar to other commands that we map their IDs
  3601.         // for the purposes of the error strings
  3602.         if (commandID == cSaveAs)
  3603.             commandID = cSave;
  3604.         if (commandID == cPrintOneCopy)
  3605.             commandID = cPrint;
  3606.             
  3607.         ConductErrorDialog(anErr, commandID, cancel);
  3608.         }
  3609.         
  3610.     // in any case, unhilite the menu selected after command processing is done    
  3611.     HiliteMenu(0);
  3612.     
  3613.     return anErr;
  3614.     
  3615. } // DoCommand
  3616.  
  3617. // --------------------------------------------------------------------------------------------------------------
  3618. #pragma segment Main
  3619.  
  3620. static OSErr    DoMenuCommand(WindowRef pWindow, long menuResult)
  3621. {
  3622.     OSErr    anErr = noErr;
  3623.     short    commandID = cNull;
  3624.     short    ** commandHandle;
  3625.     short    menuID = menuResult >> 16;
  3626.     
  3627.     if (menuID == kHMHelpMenuID)
  3628.         {
  3629.         // close existing database (if any)
  3630.         if ((gAGRefNum != -1) && AGIsDatabaseOpen(gAGRefNum))
  3631.             {
  3632.             AGClose(&gAGRefNum);
  3633.             gAGRefNum = -1;
  3634.             }
  3635.             
  3636.         // and open the database we have found in the past
  3637.         AGOpen(&gAGSpec, 0, nil, &gAGRefNum);
  3638.         }
  3639.     else
  3640.         {
  3641.         if (menuID >= mFontSubMenusStart)
  3642.             {
  3643.             commandID = cSelectFontStyle;
  3644.             }
  3645.         else
  3646.             {
  3647.             // read in the resource that controls this menu
  3648.                 {
  3649.                 short    oldResFile = CurResFile();
  3650.                 
  3651.                 UseResFile(gApplicationResFile);
  3652.                 commandHandle = (short**) Get1Resource('MCMD', menuID);
  3653.                 UseResFile(oldResFile);
  3654.                 anErr = ResError();
  3655.                 nrequire(anErr, FailedToLoadCommandTable);
  3656.                 }
  3657.             
  3658.             if (commandHandle)
  3659.                 {
  3660.                 short    item = menuResult & 0xFFFF;
  3661.                 short    * pCommands = *commandHandle;
  3662.                 
  3663.                 if (item <= pCommands[0])
  3664.                     commandID = pCommands[item];
  3665.                 else
  3666.                     commandID = pCommands[pCommands[0]];
  3667.                 }
  3668.             }
  3669.         
  3670.         anErr = DoCommand(pWindow, commandID, menuResult);
  3671.         }
  3672.     
  3673. // FALL THROUGH EXCEPTION HANDLING
  3674. FailedToLoadCommandTable:
  3675.  
  3676.     return anErr;
  3677.     
  3678. } // DoMenuCommand
  3679.  
  3680.  
  3681. // --------------------------------------------------------------------------------------------------------------
  3682. #pragma segment Main
  3683.  
  3684. static void DoKeyPageDown(WindowRef pWindow, WindowDataPtr pData, Boolean processPageControls)
  3685. {
  3686.  
  3687.     if (GetControlValue(pData->vScroll) < GetControlMaximum(pData->vScroll))
  3688.         VActionProc(pData->vScroll, kControlPageDownPart);
  3689.     else
  3690.         {
  3691.         if ( (processPageControls) && (IsCommandEnabled(cNextPage)) )
  3692.             {
  3693.             short amount;
  3694.  
  3695.             if (DoCommand(pWindow, cNextPage, 0) == eActionAlreadyHandled)
  3696.                 {
  3697.                 amount = GetControlValue(pData->vScroll);
  3698.                 SetControlAndClipAmount(pData->vScroll, &amount);
  3699.                 if (amount != 0)
  3700.                     DoScrollContent(pWindow, pData, 0, amount);
  3701.                 }
  3702.             
  3703.             AdjustMenus(pWindow, true, false);
  3704.             }
  3705.         }
  3706.     
  3707. } // DoKeyPageDown
  3708.  
  3709. // --------------------------------------------------------------------------------------------------------------
  3710. #pragma segment Main
  3711.  
  3712. static void DoKeyPageUp(WindowRef pWindow, WindowDataPtr pData, Boolean processPageControls)
  3713. {
  3714.     if (GetControlValue(pData->vScroll) > GetControlMinimum(pData->vScroll))
  3715.         VActionProc(pData->vScroll, kControlPageUpPart);
  3716.     else
  3717.         {
  3718.         if ( (processPageControls) && (IsCommandEnabled(cPreviousPage)) )
  3719.             {
  3720.             short amount;
  3721.             
  3722.             if (DoCommand(pWindow, cPreviousPage, 0) == eActionAlreadyHandled)
  3723.                 {
  3724.                 amount = -(GetControlMaximum(pData->vScroll)-GetControlValue(pData->vScroll));
  3725.                 SetControlAndClipAmount(pData->vScroll, &amount);
  3726.                 if (amount != 0)
  3727.                     DoScrollContent(pWindow, pData, 0, amount);
  3728.                 }
  3729.             
  3730.             AdjustMenus(pWindow, true, false);
  3731.             }
  3732.         }
  3733.         
  3734. } // DoKeyPageUp
  3735.  
  3736. // --------------------------------------------------------------------------------------------------------------
  3737. #pragma segment Main
  3738.  
  3739. OSErr    DoKeyEvent(WindowRef pWindow, EventRecord * pEvent, Boolean processPageControls)
  3740. {
  3741.     OSErr            anErr = noErr;
  3742.     WindowDataPtr     pData = nil;
  3743.     Boolean            passToObject = false;
  3744.     Boolean         isMotionKey = false;
  3745.     long            menuResult = 0;
  3746.     
  3747.     char keyCode = (pEvent->message >> 8) & charCodeMask;
  3748.  
  3749.     if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3750.         {
  3751.         AdjustMenus(pWindow, true, false);
  3752.         menuResult = MenuKey(pEvent->message & charCodeMask);
  3753.         DoMenuCommand(pWindow, menuResult);
  3754.         pWindow = FrontWindow();
  3755.         }
  3756.  
  3757.     if (menuResult == 0)
  3758.         {
  3759.         if (pWindow)
  3760.             {
  3761.             pData = (WindowDataPtr)GetWindowInfo(pWindow);
  3762.             if ( (pData) && (pData->pKeyEvent) )
  3763.                 passToObject = true;
  3764.             SetPort((GrafPtr) GetWindowPort(pWindow));
  3765.             }
  3766.             
  3767.         if (pData)
  3768.             {
  3769.             switch (keyCode)
  3770.                 {
  3771.                 case kHome: // top of file
  3772.                     isMotionKey = true;
  3773.                     if (pData->vScroll)
  3774.                         {
  3775.                         short amount;
  3776.                         
  3777.                         if ( (processPageControls) && (IsCommandEnabled(cGotoPage)) )
  3778.                             DoCommand(pWindow, cGotoPage, cGotoFirst);
  3779.  
  3780.                         amount = GetControlValue(pData->vScroll);
  3781.                         SetControlAndClipAmount(pData->vScroll, &amount);
  3782.                         if (amount != 0)
  3783.                             DoScrollContent(pWindow, pData, 0, amount);
  3784.                         passToObject = false;
  3785.                         }
  3786.                     break;
  3787.                     
  3788.                 case kEnd: // end of file
  3789.                     isMotionKey = true;
  3790.                     if (pData->vScroll)
  3791.                         {
  3792.                         short amount;
  3793.  
  3794.                         if ( (processPageControls) && (IsCommandEnabled(cGotoPage)) )
  3795.                             DoCommand(pWindow, cGotoPage, cGotoLast);
  3796.                             
  3797.                         amount = -(GetControlMaximum(pData->vScroll)-GetControlValue(pData->vScroll));
  3798.                         SetControlAndClipAmount(pData->vScroll, &amount);
  3799.                         if (amount != 0)
  3800.                             DoScrollContent(pWindow, pData, 0, amount);
  3801.                         passToObject = false;
  3802.                         }
  3803.                     break;
  3804.                     
  3805.                 case kPageUp: // scroll bar page up
  3806.                     isMotionKey = true;
  3807.                     if (pData->vScroll)
  3808.                         {
  3809.                         DoKeyPageUp(pWindow, pData, processPageControls);
  3810.                         passToObject = false;
  3811.                         }
  3812.                     break;
  3813.                     
  3814.                 case kPageDown: // scroll bar page down
  3815.                     isMotionKey = true;
  3816.                     if (pData->vScroll)
  3817.                         {
  3818.                         DoKeyPageDown(pWindow, pData, processPageControls);
  3819.                         passToObject = false;
  3820.                         }
  3821.                     break;
  3822.                             
  3823.                 case kUpArrow:        // scroll bar up arrow
  3824.                     isMotionKey = true;
  3825.                     if ( (pData->vScroll) && (!pData->pKeyEvent) )
  3826.                         {
  3827.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3828.                             DoKeyPageUp(pWindow, pData, processPageControls);
  3829.                         else
  3830.                             VActionProc(pData->vScroll, kControlUpButtonPart);
  3831.                         passToObject = false;
  3832.                         }
  3833.                     break;
  3834.                     
  3835.                 case kDownArrow:    // scroll bar down arrow
  3836.                     isMotionKey = true;
  3837.                     if ( (pData->vScroll) && (!pData->pKeyEvent) )
  3838.                         {
  3839.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3840.                             DoKeyPageDown(pWindow, pData, processPageControls);
  3841.                         else
  3842.                             VActionProc(pData->vScroll, kControlDownButtonPart);
  3843.                         passToObject = false;
  3844.                         }
  3845.                     break;
  3846.         
  3847.                 case kLeftArrow:    // scroll bar left arrow
  3848.                     isMotionKey = true;
  3849.                     if ( (pData->hScroll) && (!pData->pKeyEvent) )
  3850.                         {
  3851.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3852.                             HActionProc(pData->hScroll, kControlPageUpPart);
  3853.                         else
  3854.                             HActionProc(pData->hScroll, kControlUpButtonPart);
  3855.                         passToObject = false;
  3856.                         }
  3857.                     break;
  3858.                     
  3859.                 case kRightArrow:    // scroll bar right arrow
  3860.                     isMotionKey = true;
  3861.                     if ( (pData->hScroll) && (!pData->pKeyEvent) )
  3862.                         {
  3863.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3864.                             HActionProc(pData->hScroll, kControlPageDownPart);
  3865.                         else
  3866.                             HActionProc(pData->hScroll, kControlDownButtonPart);
  3867.                         passToObject = false;
  3868.                         }
  3869.                     break;
  3870.                     }
  3871.         
  3872.             if (passToObject)
  3873.                 anErr = (*(pData->pKeyEvent)) (pWindow, pData, pEvent, isMotionKey);
  3874.             else
  3875.                 {
  3876.                 if ( (pData->documentAcceptsText == false) && !( pEvent->modifiers & cmdKey ) && !(isMotionKey) )
  3877.                     anErr = eDocumentNotModifiable;
  3878.                 }
  3879.             }
  3880.  
  3881.         if ( (anErr != noErr) && (anErr != eActionAlreadyHandled) )
  3882.             ConductErrorDialog(anErr, cTypingCommand, ok);
  3883.             
  3884.         } // (menuResult == 0)
  3885.     
  3886.         
  3887.     return anErr;
  3888.     
  3889. } // DoKeyEvent
  3890.  
  3891. // --------------------------------------------------------------------------------------------------------------
  3892. #pragma segment Main
  3893.  
  3894. static OSErr DoAdjustCursor(WindowRef pWindow)
  3895. {
  3896.     OSErr        anErr = noErr;
  3897.     Point        mouse;
  3898.     Boolean        didAdjust = false;
  3899.     
  3900.     if (pWindow)
  3901.         {
  3902.         // not one of our windows?  don't do anything
  3903.         if (GetWindowKind(pWindow) != userKind)
  3904.             didAdjust = true;
  3905.             
  3906.         SetPort((GrafPtr) GetWindowPort(pWindow));
  3907.         
  3908.         if ( (!didAdjust) && (gMachineInfo.haveTSM) )
  3909.             {
  3910.             GetMouse(&mouse);
  3911.             LocalToGlobal(&mouse);
  3912.             if (SetTSMCursor(mouse))
  3913.                 didAdjust = true;
  3914.             }
  3915.             
  3916.         if (!didAdjust)
  3917.             {
  3918.             WindowDataPtr    pData = GetWindowInfo(pWindow);
  3919.             RgnHandle        content = NewRgn();
  3920.             Point            globalMouse;
  3921.             
  3922.             GetMouse(&mouse);
  3923.             globalMouse = mouse;
  3924.             LocalToGlobal(&globalMouse);
  3925.             
  3926.             GetWindowContentRgn(pWindow, content);
  3927.             if ((pData) && (PtInRgn(globalMouse, content)) && (PtInRect(mouse, &pData->contentRect)))
  3928.                 {
  3929.                 Rect            tempRect;
  3930.                 
  3931.                 tempRect = pData->contentRect;
  3932.                 LocalToGlobal(&TopLeft(tempRect));
  3933.                 LocalToGlobal(&BotRight(tempRect));
  3934.                 
  3935.                 if (pData->pAdjustCursor)
  3936.                     anErr = (*(pData->pAdjustCursor)) (pWindow, pData, &mouse, &tempRect);
  3937.     
  3938.                 RectRgn(gCursorRgn, &tempRect);
  3939.                 }
  3940.             DisposeRgn(content);
  3941.             }
  3942.         else
  3943.             anErr = eActionAlreadyHandled;
  3944.         }
  3945.     
  3946.     // nobody set the cursor, we do it ourselves
  3947.     if (anErr != eActionAlreadyHandled)
  3948.         SetCursor(&qd.arrow);
  3949.         
  3950.     return anErr;
  3951.     
  3952. } // DoAdjustCursor
  3953.  
  3954. // --------------------------------------------------------------------------------------------------------------
  3955. #pragma segment Main
  3956.  
  3957. static long DetermineWaitTime(WindowRef pWindow)
  3958. {
  3959.     long    waitTime = kMaxWaitTime;
  3960.     
  3961.     while (pWindow)
  3962.         {
  3963.         long            newWaitTime;
  3964.         WindowDataPtr    pData = GetWindowInfo(pWindow);
  3965.         
  3966.         if ((pData) && (pData->pCalculateIdleTime))
  3967.             newWaitTime = (*(pData->pCalculateIdleTime)) (pWindow, pData);
  3968.         else
  3969.             newWaitTime = kMaxWaitTime;
  3970.         
  3971.         if (newWaitTime < waitTime)
  3972.             waitTime = newWaitTime;
  3973.             
  3974.         pWindow = GetNextWindow(pWindow);
  3975.         }
  3976.     
  3977.     return(waitTime);
  3978.     
  3979. } // DetermineWaitTime
  3980.  
  3981. // --------------------------------------------------------------------------------------------------------------
  3982. #pragma segment Main
  3983.  
  3984. void HandleEvent(EventRecord * pEvent)
  3985. {
  3986.     WindowRef pWindow = FrontWindow();
  3987.     
  3988.     switch (pEvent->what)
  3989.         {
  3990.         case kHighLevelEvent:
  3991.             AEProcessAppleEvent(pEvent);
  3992.             break;
  3993.             
  3994.         case osEvt:
  3995.             switch ((pEvent->message >> 24) & 0xFF) /* high byte of message */
  3996.                 {        
  3997.                 case mouseMovedMessage:
  3998.                     DoAdjustCursor(pWindow);
  3999.                     break;
  4000.                     
  4001.                 case suspendResumeMessage:        /* suspend/resume is also an activate/deactivate */
  4002.                     gMachineInfo.amInBackground = (pEvent->message & 1) == 0;
  4003.                     if (pWindow)
  4004.                         DoActivate(pWindow, !gMachineInfo.amInBackground);
  4005.                         
  4006.                     // on resume, we must call GXUpdateJob for all active jobs in
  4007.                     // order to get instant activation of extensions and to properly
  4008.                     // handle potential shifting of driver names
  4009.                     if ( (gMachineInfo.haveGX) && (!gMachineInfo.amInBackground) )
  4010.                         {
  4011.                         WindowRef    walkWindows = pWindow;
  4012.                         
  4013.                         while (walkWindows)
  4014.                             {
  4015.                             WindowDataPtr    pData = GetWindowInfo(walkWindows);
  4016.                             if (pData)
  4017.                                 {
  4018.                                 gxJob    theJob = pData->hPrint;
  4019.                                 if (theJob)
  4020.                                     {
  4021.                                     GXUpdateJob(theJob);
  4022.                                     }
  4023.                                 }
  4024.                             walkWindows = GetNextWindow(walkWindows);
  4025.                             }
  4026.                         }
  4027.                     break;
  4028.                 }
  4029.             break;
  4030.             
  4031.         case activateEvt:
  4032.             pWindow = (WindowRef) pEvent->message;
  4033.             DoActivate(pWindow, (pEvent->modifiers & activeFlag) != 0);
  4034.             break;
  4035.                             
  4036.         // disk inserted events must be handled, or uninitialized floppies 
  4037.         // won't be recognized.
  4038.         case diskEvt:
  4039.             if ( HiWord(pEvent->message) != noErr ) 
  4040.                 {
  4041.                 Point    where;
  4042.             
  4043.                 SetPt(&where, 70, 50);
  4044.                 ShowCursor();
  4045.                 (void) DIBadMount(where, pEvent->message);
  4046.                 }        
  4047.             break;
  4048.                 
  4049.         case mouseUp:
  4050.             break;
  4051.             
  4052.         case mouseDown:
  4053.             {
  4054.             short part = FindWindow(pEvent->where, &pWindow);                    
  4055.             
  4056.             switch ( part ) 
  4057.                 {
  4058.                 case inContent:
  4059.                     if (pWindow != FrontWindow())
  4060.                         SelectWindow(pWindow);
  4061.                     else
  4062.                         DoContentClick(pWindow);
  4063.                     break;
  4064.                     
  4065.                 case inGoAway:
  4066.                     if (TrackGoAway(pWindow, pEvent->where) )
  4067.                         DoCommand(pWindow, cClose, 0);
  4068.                     break;
  4069.                     
  4070.                 case inGrow:
  4071.                     DoGrowWindow(pWindow, pEvent);
  4072.                     break;
  4073.                     
  4074.                 case inZoomIn:
  4075.                 case inZoomOut:
  4076.                     if ( TrackBox(pWindow, pEvent->where, part) )
  4077.                         DoZoomWindow(pWindow, part);
  4078.                     break;
  4079.                     
  4080.                 case inDrag:
  4081.                     {
  4082.                     WindowDataPtr    pData = GetWindowInfo(pWindow);
  4083.                     
  4084.                     if ( (pData) && (pData->dragWindowAligned) )
  4085.                         DragAlignedWindow((WindowPtr) pWindow, pEvent->where, &qd.screenBits.bounds, nil, nil);
  4086.                     else
  4087.                         DragWindow(pWindow, pEvent->where, &qd.screenBits.bounds);
  4088.                     }
  4089.                     break;
  4090.                     
  4091.                 case inMenuBar:                /* process a mouse menu command (if any) */
  4092.                     {
  4093.                     long    menuResult;
  4094.                     
  4095.                     // force these threads to run to completion so the
  4096.                     // contents of the menus are fully initialized
  4097.                     
  4098.                     if (gFontThread != kNoThreadID)
  4099.                         {
  4100.                         gDontYield = true;
  4101.                         SetThreadState(gFontThread, kReadyThreadState, gFontThread);
  4102.                         YieldToThread(gFontThread);
  4103.                         gDontYield = false;
  4104.                         }
  4105.                     if (gAGThread != kNoThreadID)
  4106.                         {
  4107.                         gDontYield = true;
  4108.                         SetThreadState(gAGThread, kReadyThreadState, gAGThread);
  4109.                         YieldToThread(gAGThread);
  4110.                         gDontYield = false;
  4111.                         }
  4112.                     
  4113.                     pWindow = FrontWindow();
  4114.                     AdjustMenus(pWindow, true, false);
  4115.                     menuResult = MenuSelect(pEvent->where);
  4116.                     if ( (gMachineInfo.haveTSM) && (TSMMenuSelect(menuResult)) )
  4117.                         HiliteMenu(0);
  4118.                     else
  4119.                         DoMenuCommand(pWindow, menuResult);
  4120.                     }
  4121.                     break;
  4122.                     
  4123.                 case inSysWindow:            /* let the system handle the mouseDown */
  4124.                     SystemClick(pEvent, pWindow);
  4125.                     break;
  4126.                     
  4127.                 } // switch(part)
  4128.             }
  4129.             break;
  4130.             
  4131.         case keyDown:
  4132.         case autoKey:                        /* check for menukey equivalents */
  4133.             DoKeyEvent(pWindow, pEvent, true);
  4134.             break;
  4135.             
  4136.         case updateEvt:
  4137.             pWindow = (WindowRef) pEvent->message;
  4138.             DoUpdateWindow(pWindow);
  4139.             break;
  4140.  
  4141.         } // switch (pEvent->what)
  4142.     
  4143. } // HandleEvent
  4144.  
  4145. // --------------------------------------------------------------------------------------------------------------
  4146. #pragma segment Main
  4147.  
  4148. static OSErr    DoEventLoop(void)
  4149. {
  4150.     OSErr        anErr = noErr;
  4151.     Boolean        gotEvent;
  4152.     Boolean        trueGotEvent;
  4153.     WindowRef    pWindow;
  4154.     
  4155.     do     {
  4156.         pWindow = LMGetFirstWindow();        // walk all of our windows, even invisible ones
  4157.         
  4158.         DoAdjustCursor(pWindow);
  4159.         gotEvent = WaitNextEvent(everyEvent, &gEvent, DetermineWaitTime(pWindow), gCursorRgn);
  4160.         trueGotEvent = gotEvent;
  4161.  
  4162.         // WNE may close the window if it's owned by some silly extension.
  4163.         pWindow = LMGetFirstWindow();        
  4164.         
  4165.         // let text services handle the event first if it wishes to do so
  4166.         if ( gMachineInfo.haveTSM )
  4167.             {
  4168.             ScriptCode    keyboardScript;
  4169.             WindowRef    theFront = FrontWindow();
  4170.             
  4171.             if (theFront)
  4172.                 {
  4173.                 SetPort((GrafPtr) GetWindowPort(theFront));
  4174.                 
  4175.                 keyboardScript = GetScriptManagerVariable(smKeyScript);
  4176.                 if (FontToScript(qd.thePort->txFont) != keyboardScript)
  4177.                     TextFont(GetScriptVariable(keyboardScript, smScriptAppFond));
  4178.                 }
  4179.             
  4180.             if (TSMEvent(&gEvent))
  4181.                 gotEvent = false;
  4182.             }
  4183.             
  4184.         // let all windows filter this event, and get time if they wish to
  4185.         while (pWindow)
  4186.             {
  4187.             WindowDataPtr    pData = GetWindowInfo(pWindow);
  4188.             Boolean            finishedEvent = false;
  4189.             
  4190.             // help manager for the front window
  4191.             if ( (pWindow == FrontWindow()) && (pData) && (!gMachineInfo.amInBackground) && (HMGetBalloons()) )
  4192.                 {
  4193.                 Point    theMouse, tipLocation;
  4194.                 short    newBalloon = iNoBalloon;
  4195.                 Rect    tempRect;
  4196.                 
  4197.                 // find out where the mouse is                
  4198.                 SetPort((GrafPtr) GetWindowPort(pWindow));
  4199.                 GetMouse(&theMouse);
  4200.                 
  4201.                 // and only do something if we are within the window itself
  4202.                 if (PtInRect(theMouse, &GetWindowPort(pWindow)->portRect))
  4203.                     {
  4204.                     // is it in the vertical scroll bar?
  4205.                     if (pData->vScroll)
  4206.                         {
  4207.                         tempRect = (**(pData->vScroll)).contrlRect;
  4208.                         if (PtInRect(theMouse, &tempRect))
  4209.                             {
  4210.                             newBalloon = iHelpActiveScroll;
  4211.                             if (GetControlMinimum(pData->vScroll) == GetControlMaximum(pData->vScroll))
  4212.                                 newBalloon = iHelpDimVertScroll;
  4213.                             tipLocation.h = tempRect.right - kFromBottomTipOffset;
  4214.                             tipLocation.v = tempRect.bottom - kFromBottomTipOffset;
  4215.                             }
  4216.                         }
  4217.                         
  4218.                     // is it in the horizontal scroll bar?
  4219.                     if (pData->hScroll)
  4220.                         {
  4221.                         tempRect = (**(pData->hScroll)).contrlRect;
  4222.                         if (PtInRect(theMouse, &tempRect))
  4223.                             {
  4224.                             newBalloon = iHelpActiveScroll;
  4225.                             if (GetControlMinimum(pData->hScroll) == GetControlMaximum(pData->hScroll))
  4226.                                 newBalloon = iHelpDimHorizScroll;
  4227.                             tipLocation.h = tempRect.right - kFromBottomTipOffset;
  4228.                             tipLocation.v = tempRect.bottom - kFromBottomTipOffset;
  4229.                             }
  4230.                         }
  4231.                     
  4232.                     // is it in the grow box?
  4233.                     if (pData->hasGrow)
  4234.                         {
  4235.                         CalculateGrowIcon(pData, &tempRect);
  4236.                         if (PtInRect(theMouse, &tempRect))
  4237.                             {
  4238.                             newBalloon = iHelpGrowBox;
  4239.                             tipLocation.h = tempRect.right - kFromBottomTipOffset;
  4240.                             tipLocation.v = tempRect.bottom - kFromBottomTipOffset;
  4241.                             }
  4242.                         }
  4243.                     
  4244.                     // none of the above, must be the content
  4245.                     if (newBalloon == iNoBalloon)
  4246.                         {
  4247.                         newBalloon = iHelpGenericContent;
  4248.                         tempRect = pData->contentRect;
  4249.                         if (pData->pGetBalloon)
  4250.                             (*(pData->pGetBalloon)) (pWindow, pData, &theMouse, &newBalloon, &tempRect);
  4251.                             
  4252.                         tipLocation.h = tempRect.left + kFromTopTipOffset;
  4253.                         tipLocation.v = tempRect.top + kFromTopTipOffset;
  4254.                         }
  4255.                         
  4256.                     // show our new balloon, or remove the old one
  4257.                     if (newBalloon != iNoBalloon)
  4258.                         {
  4259.                         if ( (gMachineInfo.lastBalloonIndex != newBalloon) || (!HMIsBalloon()) )
  4260.                             {
  4261.                             HMMessageRecord    message;
  4262.                             
  4263.                             if (newBalloon != iDidTheBalloon)
  4264.                                 {
  4265.                                 message.hmmHelpType = khmmString;
  4266.                                 GetIndString(message.u.hmmString, kWindowHelpID, newBalloon);
  4267.                                 LocalToGlobal(&tipLocation);
  4268.                                 (void) HMShowBalloon(&message, tipLocation, nil, nil, 0, kDefaultBalloonVariant, 0);
  4269.                                 }
  4270.                             gMachineInfo.lastBalloonIndex = newBalloon;
  4271.                             }
  4272.                         }
  4273.                     else
  4274.                         HMRemoveBalloon();
  4275.                     }
  4276.                     
  4277.                     
  4278.                 }
  4279.                 
  4280.             // if we hit a window we know about, then do filtering
  4281.             if (pData)
  4282.                 {
  4283.                 if (pData->pFilterEvent)
  4284.                     finishedEvent = (*(pData->pFilterEvent)) (pWindow, pData, &gEvent);
  4285.                 }
  4286.  
  4287.             // if filtering indicates complete handling of event, then stop, and
  4288.             // do no regular processing.
  4289.             if (finishedEvent)
  4290.                 {
  4291.                 gotEvent = false;
  4292.                 pWindow = nil;
  4293.                 }
  4294.             else
  4295.                 pWindow = GetNextWindow(pWindow);
  4296.             }
  4297.             
  4298.         if (gotEvent)
  4299.             HandleEvent(&gEvent);
  4300.             
  4301.         // close request?
  4302.         if (gAllDone)
  4303.             {
  4304.             pWindow = FrontWindow();
  4305.             while ((gAllDone) && (pWindow) )
  4306.                 {
  4307.                 WindowRef    nextWindow = GetNextWindow(pWindow);
  4308.                 OSErr        closeError = DoCloseWindow(pWindow);
  4309.                 
  4310.                 // window didn't close?  then don't quit
  4311.                 if (pWindow == FrontWindow())
  4312.                     gAllDone = false;
  4313.                     
  4314.                 // something bad happened, then don't quit
  4315.                 if ( (closeError != noErr) /* && (closeError != eUserCanceled) */ )
  4316.                     gAllDone = false;
  4317.                     
  4318.                 pWindow = nextWindow;
  4319.                 }
  4320.             }
  4321.         
  4322.         // our threads are low-priority, so we only give time to them on idle
  4323.         if (gMachineInfo.haveThreads && !trueGotEvent && !gAllDone)
  4324.             YieldToAnyThread();
  4325.         
  4326.         } while (!gAllDone);
  4327.         
  4328.     return anErr;
  4329.     
  4330. } // DoEventLoop
  4331.  
  4332.  
  4333. // --------------------------------------------------------------------------------------------------------------
  4334. // DRAG MANAGEMENT GLOBAL SUPPORT ROUTINES
  4335. // --------------------------------------------------------------------------------------------------------------
  4336.  
  4337. // Globals for our drag handlers
  4338.  
  4339. Boolean                gCanAccept;                // if we can receive the item(s) being dragged
  4340.  
  4341. // --------------------------------------------------------------------------------------------------------------
  4342. #pragma segment Drag
  4343.  
  4344. static pascal OSErr GlobalTrackingHandler(short message, WindowRef pWindow, void *handlerRefCon, DragReference theDragRef)
  4345. {
  4346.     #pragma unused(handlerRefCon)
  4347.  
  4348.     WindowDataPtr pData = GetWindowInfo(pWindow);
  4349.  
  4350.     // Call the tracking handler associated with this type of window. Only allow messages referencing
  4351.     // a specific window to be passed to the handler.
  4352.  
  4353.     if (pData)
  4354.         {    
  4355.         if (pData->pDragTracking)
  4356.             return ((*(pData->pDragTracking)) (pWindow, pData, theDragRef, message));
  4357.         }
  4358.     
  4359.     return noErr;
  4360.  
  4361. } // GlobalTrackingHandler
  4362.  
  4363. DragTrackingHandlerUPP gGlobalTrackingHandler;
  4364.  
  4365. // --------------------------------------------------------------------------------------------------------------
  4366. #pragma segment Drag
  4367.  
  4368. static pascal OSErr GlobalReceiveHandler(WindowRef pWindow, void *handlerRefCon, DragReference theDragRef)
  4369. {
  4370.     #pragma unused(handlerRefCon)
  4371.  
  4372.     WindowDataPtr pData = GetWindowInfo(pWindow);
  4373.     
  4374.     if (pData)
  4375.         {
  4376.         if (pData->pDragTracking)
  4377.             return ((*(pData->pDragReceive)) (pWindow, pData, theDragRef));
  4378.         }
  4379.  
  4380.     return noErr;
  4381.  
  4382. } // GlobalReceiveHandler
  4383.  
  4384. DragReceiveHandlerUPP gGlobalReceiveHandler;
  4385.  
  4386. // --------------------------------------------------------------------------------------------------------------
  4387. //
  4388. // IsOnlyThisFlavor - Given a DragReference and a FlavorType, we iterate through the drag items to determine if
  4389. //                      all are of flavor theType. If this is so, we return true. If any of the items are not
  4390. //                      theType, we return false, indicating that we should not accept the drag.
  4391. //
  4392. #pragma segment Drag
  4393.  
  4394. Boolean IsOnlyThisFlavor(DragReference theDragRef, FlavorType theType)
  4395. {
  4396.     unsigned short    items, index;
  4397.     FlavorFlags        theFlags;
  4398.     ItemReference    itemID;
  4399.     OSErr            anErr = noErr;
  4400.  
  4401.     CountDragItems(theDragRef, &items);
  4402.     
  4403.     for(index = 1; index <= items; index++)
  4404.         {
  4405.         GetDragItemReferenceNumber(theDragRef, index, &itemID);
  4406.  
  4407.         anErr = GetFlavorFlags(theDragRef, itemID, theType, &theFlags);
  4408.         if(anErr == noErr)
  4409.             continue;    // it's okay, this flavor is cool
  4410.  
  4411.         return false;    // this item has at least one flavor we don't like
  4412.         }
  4413.  
  4414.     return true;        // all flavors in this item were cool
  4415.  
  4416. } // IsOnlyThisFlavor
  4417.  
  4418. // --------------------------------------------------------------------------------------------------------------
  4419. //
  4420. // IsDropInFinderTrash - Returns true if the given dropLocation AEDesc is a descriptor of the Finder's Trash.
  4421. //
  4422. #pragma segment Drag
  4423.  
  4424. Boolean IsDropInFinderTrash(AEDesc *dropLocation)
  4425. {
  4426.     OSErr            result;
  4427.     AEDesc            dropSpec;
  4428.     FSSpec            *theSpec;
  4429.     CInfoPBRec        thePB;
  4430.     short            trashVRefNum;
  4431.     long            trashDirID;
  4432.  
  4433.     //    Coerce the dropLocation descriptor into an FSSpec. If there's no dropLocation or
  4434.     //    it can't be coerced into an FSSpec, then it couldn't have been the Trash.
  4435.  
  4436.     if ((dropLocation->descriptorType != typeNull) &&
  4437.         (AECoerceDesc(dropLocation, typeFSS, &dropSpec) == noErr)) 
  4438.         {
  4439.         unsigned char flags = HGetState(dropSpec.dataHandle);
  4440.         
  4441.         HLock(dropSpec.dataHandle);
  4442.         theSpec = (FSSpec *) *dropSpec.dataHandle;
  4443.  
  4444.         //    Get the directory ID of the given dropLocation object.
  4445.  
  4446.         thePB.dirInfo.ioCompletion = 0L;
  4447.         thePB.dirInfo.ioNamePtr = (StringPtr) &theSpec->name;
  4448.         thePB.dirInfo.ioVRefNum = theSpec->vRefNum;
  4449.         thePB.dirInfo.ioFDirIndex = 0;
  4450.         thePB.dirInfo.ioDrDirID = theSpec->parID;
  4451.  
  4452.         result = PBGetCatInfoSync(&thePB);
  4453.  
  4454.         HSetState(dropSpec.dataHandle, flags);
  4455.         AEDisposeDesc(&dropSpec);
  4456.  
  4457.         if (result != noErr)
  4458.             return false;
  4459.  
  4460.         //    If the result is not a directory, it must not be the Trash.
  4461.  
  4462.         if (!(thePB.dirInfo.ioFlAttrib & (1 << 4)))
  4463.             return false;
  4464.  
  4465.         //    Get information about the Trash folder.
  4466.  
  4467.         FindFolder(theSpec->vRefNum, kTrashFolderType, kCreateFolder, &trashVRefNum, &trashDirID);
  4468.  
  4469.         //    If the directory ID of the dropLocation object is the same as the directory ID
  4470.         //    returned by FindFolder, then the drop must have occurred into the Trash.
  4471.  
  4472.         if (thePB.dirInfo.ioDrDirID == trashDirID)
  4473.             return true;
  4474.         }
  4475.  
  4476.     return false;
  4477.  
  4478. } // IsDropInFinderTrash
  4479.  
  4480. // --------------------------------------------------------------------------------------------------------------
  4481. // APPLE EVENT SUPPORT ROUTINES
  4482. // --------------------------------------------------------------------------------------------------------------
  4483. #pragma segment Main
  4484.  
  4485. static OSErr    MissingParameterCheck(
  4486.     AppleEvent     *inputEvent)
  4487. /*
  4488.     This routine checks an input AppleEvent for the missing keyword.
  4489.     If the missing keyword is found, that means that some required
  4490.     parameters were missing (ie, an error). 
  4491.     
  4492.     However, if the missing keyword isn't found, that means that we aren't missing 
  4493.     any required parameters (that is to say, all REQUIRED parameters were supplied
  4494.     by the person who created the event).
  4495.     
  4496.     SOME DAY, THE ABOVE COMMENT WILL MAKE SENSE TO YOU.  IT STILL DOESN'T
  4497.     TO ME AND I WAS THE ONE WHO WROTE IT.
  4498. */
  4499. {
  4500.     OSErr        anErr;
  4501.     AEKeyword    missingKeyword;
  4502.     DescType    ignoredActualType;
  4503.     Size        ignoredActualSize;
  4504.     
  4505.     anErr = AEGetAttributePtr(
  4506.         inputEvent, 
  4507.         keyMissedKeywordAttr,
  4508.         typeWildCard,
  4509.         &ignoredActualType,
  4510.         (Ptr) &missingKeyword,
  4511.         sizeof(AEKeyword),
  4512.         &ignoredActualSize);
  4513.             
  4514.     if (anErr == noErr)
  4515.         anErr = errAEParamMissed;
  4516.     else
  4517.         if (anErr == errAEDescNotFound)
  4518.             anErr = noErr;
  4519.         
  4520.     return anErr;
  4521.     
  4522. } // MissingParameterCheck
  4523.  
  4524. // --------------------------------------------------------------------------------------------------------------
  4525. // Globals for our handlers
  4526. Boolean gQuitAfterPrint = true;
  4527.  
  4528. // --------------------------------------------------------------------------------------------------------------
  4529. #pragma segment Main
  4530.  
  4531. static pascal OSErr    DoOpenApp(
  4532.     AppleEvent     *inputEvent,
  4533.     AppleEvent     *outputEvent,
  4534.     long        handlerRefCon)
  4535. {
  4536. #pragma unused (outputEvent, handlerRefCon)
  4537.  
  4538.     DoCommand(nil, cNew, 0);
  4539.     gQuitAfterPrint = false;
  4540.     
  4541.     // so that the initial document opens more quickly, we don't start
  4542.     // the threads until we get an OpenApp or OpenDocument AppleEvent
  4543.     if (gStarterThread != kNoThreadID)
  4544.         SetThreadState(gStarterThread, kReadyThreadState, gStarterThread);
  4545.     
  4546.     return(MissingParameterCheck(inputEvent));
  4547.     
  4548. } // DoAppOpen
  4549.  
  4550. // --------------------------------------------------------------------------------------------------------------
  4551. #pragma segment Main
  4552.  
  4553. static pascal OSErr    DoQuitApp(
  4554.     AppleEvent     *inputEvent,
  4555.     AppleEvent     *outputEvent,
  4556.     long        handlerRefCon)
  4557. {
  4558. #pragma unused (outputEvent, handlerRefCon)
  4559.  
  4560.     DoCommand(nil, cQuit, 0);
  4561.  
  4562.     return(MissingParameterCheck(inputEvent));
  4563.     
  4564. } // DoQuitApp
  4565.  
  4566. // --------------------------------------------------------------------------------------------------------------
  4567. #pragma segment Main
  4568.  
  4569. static pascal OSErr    DoOpenOrPrint(
  4570.     AppleEvent     *inputEvent,
  4571.     StringPtr    pPrinterName)    // nil == 0, zero length == print to default, other == printer name
  4572. {
  4573.  
  4574.     OSErr        anErr, anErr2;
  4575.     AEDescList    docList;                // list of docs passed in
  4576.     long        index, itemsInList;
  4577.     void*        hPrint;
  4578.     Boolean        wasAlreadyOpen;
  4579.     
  4580.     anErr = AEGetParamDesc( inputEvent, keyDirectObject, typeAEList, &docList);
  4581.     nrequire(anErr, GetFileList);
  4582.  
  4583.     anErr = AECountItems( &docList, &itemsInList);            // how many files passed in
  4584.     nrequire(anErr, CountDocs);
  4585.     for (index = 1; index <= itemsInList; index++)            // handle each file passed in
  4586.         {    
  4587.         AEKeyword    keywd;
  4588.         DescType    returnedType;
  4589.         Size        actualSize;
  4590.         FSSpec        theFSS;    
  4591.  
  4592.         anErr = AEGetNthPtr( &docList, index, typeFSS, &keywd, &returnedType,    // get file's info
  4593.                             (Ptr)(&theFSS), sizeof(theFSS), &actualSize);
  4594.         nrequire(anErr, AEGetNthPtr);
  4595.         
  4596.         {
  4597.         FInfo    theFileInfo;
  4598.         
  4599.         anErr = FSpGetFInfo(&theFSS, &theFileInfo);
  4600.         if (anErr == noErr)
  4601.             anErr = DetermineWindowTypeOrOpen(&theFSS, theFileInfo.fdType, nil, nil, &wasAlreadyOpen);
  4602.             
  4603.         if (anErr == eDocumentWrongKind)
  4604.             {
  4605.             if (pPrinterName)
  4606.                 ConductErrorDialog(anErr, cPrint, cancel);
  4607.             else
  4608.                 ConductErrorDialog(anErr, cOpen, cancel);
  4609.  
  4610.             anErr = noErr;
  4611.             break;
  4612.             }
  4613.             
  4614.         nrequire(anErr, DetermineWindowTypeOrOpen);
  4615.         }
  4616.         
  4617.         if (pPrinterName)
  4618.             {
  4619.             WindowRef        pWindow = FrontWindow();
  4620.             WindowDataPtr    pData = GetWindowInfo(pWindow);
  4621.             
  4622.             if (pData->pPrintPage)
  4623.                 {
  4624.                 if (index == 1)
  4625.                     {
  4626.                     anErr = DoPrintSetup(pWindow, pPrinterName);
  4627.                     if (anErr == noErr)
  4628.                         hPrint = pData->hPrint;
  4629.                     }
  4630.                     
  4631.                 if (anErr == noErr)
  4632.                     anErr = DoPrint(pWindow, hPrint, false);
  4633.                     
  4634.                 if (index != itemsInList)
  4635.                     pData->hPrint = nil;
  4636.                 }
  4637.             
  4638.             if (!wasAlreadyOpen)
  4639.                 DoCloseWindow(pWindow);
  4640.  
  4641.             if (anErr != noErr)
  4642.                 break;
  4643.             }
  4644.         }
  4645.  
  4646.     // finally, make sure we didn't miss any parameters
  4647.     anErr2 = MissingParameterCheck(inputEvent);
  4648.     if (anErr == noErr)
  4649.         anErr = anErr2;
  4650.         
  4651. // FALL THROUGH EXCEPTION HANDLING
  4652. DetermineWindowTypeOrOpen:
  4653. AEGetNthPtr:
  4654. CountDocs:
  4655.     // done with doc list
  4656.     (void) AEDisposeDesc( &docList);                        
  4657.     
  4658. GetFileList:
  4659.  
  4660.     // don't report cancels from prints
  4661.     if (pPrinterName)
  4662.         {
  4663.         if ( (anErr == iPrAbort) || (anErr == gxPrUserAbortErr) )
  4664.             anErr = noErr;
  4665.         }
  4666.     
  4667.     if ( (anErr != noErr) && (anErr != eActionAlreadyHandled) && (anErr != eUserCanceled) )
  4668.         {
  4669.         if (pPrinterName)
  4670.             ConductErrorDialog(anErr, cPrint, cancel);
  4671.         else
  4672.             ConductErrorDialog(anErr, cOpen, cancel);
  4673.         }
  4674.         
  4675.     return anErr;
  4676.     
  4677. } // DoOpenOrPrint
  4678.  
  4679. // --------------------------------------------------------------------------------------------------------------
  4680. #pragma segment Main
  4681.  
  4682. static pascal OSErr    DoOpenDocument(
  4683.     AppleEvent     *inputEvent,
  4684.     AppleEvent     *outputEvent,
  4685.     long        handlerRefCon)
  4686. {
  4687. #pragma unused (outputEvent, handlerRefCon)
  4688.  
  4689.     OSErr        anErr;
  4690.     
  4691.     if (IsCommandEnabled(cOpen))
  4692.         {
  4693.         gQuitAfterPrint = false;
  4694.         anErr = DoOpenOrPrint(inputEvent, nil);
  4695.         }
  4696.     else
  4697.         {
  4698.         anErr = errAEEventNotHandled;
  4699.         ConductErrorDialog(anErr, cOpen, cancel);
  4700.         }
  4701.         
  4702.     // so that the initial document opens more quickly, we don't start
  4703.     // the threads until we get an OpenApp or OpenDocument AppleEvent
  4704.     if (gStarterThread != kNoThreadID)
  4705.         SetThreadState(gStarterThread, kReadyThreadState, gStarterThread);
  4706.     
  4707.     return anErr;
  4708.     
  4709. } // DoOpenDocument
  4710.  
  4711. // --------------------------------------------------------------------------------------------------------------
  4712. #pragma segment Main
  4713.  
  4714. static pascal OSErr    DoPrintDocument(
  4715.     AppleEvent     *inputEvent,
  4716.     AppleEvent     *outputEvent,
  4717.     long        handlerRefCon)
  4718. {
  4719. #pragma unused (outputEvent, handlerRefCon)
  4720.     OSErr        anErr;
  4721.     FSSpec        printerFSS;
  4722.     AEDescList    dtpList;                // list of docs passed in
  4723.     
  4724.     if (IsCommandEnabled(cOpen))
  4725.         {
  4726.         // try to find out if this doc was dropped onto a printer
  4727.         anErr = AEGetAttributeDesc( inputEvent, keyOptionalKeywordAttr, typeAEList, &dtpList);
  4728.     
  4729.         if (anErr == noErr)                                            // doc dragged to dtp?
  4730.             {
  4731.             AEKeyword    keywd;
  4732.             DescType    returnedType;
  4733.             Size        actualSize;
  4734.     
  4735.             anErr = AEGetNthPtr( &dtpList, 1, typeFSS, &keywd, &returnedType,    // get dtp info
  4736.                             (Ptr)(&printerFSS), sizeof(printerFSS), &actualSize);
  4737.             }
  4738.             
  4739.         // if it wasn't, that's not an error, just print normally
  4740.         if (anErr != noErr)
  4741.             {
  4742.             printerFSS.name[0] = 0;
  4743.             anErr = noErr;
  4744.             }
  4745.             
  4746.         anErr = DoOpenOrPrint(inputEvent, &printerFSS.name[0]);
  4747.         
  4748.         // if we are opened just for printing -- quit afterwards
  4749.         if (gQuitAfterPrint)
  4750.             DoCommand(nil, cQuit, 0);
  4751.         }
  4752.     else
  4753.         {
  4754.         anErr = errAEEventNotHandled;
  4755.         ConductErrorDialog(anErr, cPrint, cancel);
  4756.         }
  4757.         
  4758.     return anErr;
  4759.     
  4760. } // DoPrintDocument
  4761.  
  4762. #if GENERATINGCFM
  4763.     static RoutineDescriptor    gDoOpenAppRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoOpenApp);
  4764.     static AEEventHandlerUPP    gDoOpenApp = &gDoOpenAppRD;
  4765.  
  4766.     static RoutineDescriptor    gDoQuitAppRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoQuitApp);
  4767.     static AEEventHandlerUPP    gDoQuitApp = &gDoQuitAppRD;
  4768.  
  4769.     static RoutineDescriptor    gDoOpenDocumentRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoOpenDocument);
  4770.     static AEEventHandlerUPP    gDoOpenDocument = &gDoOpenDocumentRD;
  4771.  
  4772.     static RoutineDescriptor    gDoPrintDocumentRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoPrintDocument);
  4773.     static AEEventHandlerUPP    gDoPrintDocument = &gDoPrintDocumentRD;
  4774. #else
  4775.     static AEEventHandlerUPP    gDoOpenApp = (AEEventHandlerUPP) DoOpenApp;
  4776.     static AEEventHandlerUPP    gDoQuitApp = (AEEventHandlerUPP) DoQuitApp;
  4777.     static AEEventHandlerUPP    gDoOpenDocument = (AEEventHandlerUPP) DoOpenDocument;
  4778.     static AEEventHandlerUPP    gDoPrintDocument = (AEEventHandlerUPP) DoPrintDocument;
  4779. #endif
  4780. // --------------------------------------------------------------------------------------------------------------
  4781. #pragma segment Main
  4782.  
  4783. static pascal OSErr SimpleTextCoachHandler(Rect *pRect, Ptr name, long refCon)
  4784. {
  4785. #pragma unused (refCon)
  4786.  
  4787.     OSErr            anErr = noErr;
  4788.     WindowRef        pWindow = FrontWindow();
  4789.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  4790.     
  4791.     if ((pData) && (pData->pGetCoachRectangle))
  4792.         anErr = (*(pData->pGetCoachRectangle)) (pWindow, pData, pRect, name);
  4793.         
  4794.     return(anErr);
  4795.     
  4796. } // SimpleTextCoachHandler
  4797.  
  4798. // --------------------------------------------------------------------------------------------------------------
  4799. // MAIN INITIALIZE/SHUTDOWN/LOOP ROUTINES
  4800. // --------------------------------------------------------------------------------------------------------------
  4801. #pragma segment Main
  4802.  
  4803. static pascal void* StarterThread(void* threadParam)
  4804. {
  4805.     #pragma unused(threadParam)
  4806.     
  4807.     /*
  4808.         All threads, including the starter thread, are initially created
  4809.         in a suspended state. The starter thread is made ready to run when
  4810.         we get an open application or open document event. It runs until
  4811.         there are no activate or update events pending, and then starts the
  4812.         font menu and Apple Guide threads. This gives much better performance
  4813.         for the initial creation and update of a document, because the threads
  4814.         (especially the font thread) chew up a lot of time at first.
  4815.         
  4816.         The starter thread isn't really necessary - we could get the same
  4817.         effect by just checking in the event loop for activate/update events -
  4818.         but hey, it's a lot easier to do it this way, doesn't cost much, and
  4819.         isn't that what threads are for?
  4820.     */
  4821.     
  4822.     for (;;)
  4823.         {
  4824.         EventRecord        er;
  4825.     
  4826.         YieldToAnyThread();
  4827.         
  4828.         if (!EventAvail(activMask | updateMask, &er))
  4829.             {
  4830.             if (gFontThread != kNoThreadID)
  4831.                 SetThreadState(gFontThread, kReadyThreadState, gFontThread);
  4832.             if (gAGThread != kNoThreadID)
  4833.                 SetThreadState(gAGThread, kReadyThreadState, gAGThread);
  4834.             
  4835.             break;
  4836.             }
  4837.         }
  4838.     
  4839.     gStarterThread = kNoThreadID;
  4840.     return NULL;
  4841.     
  4842. } // StarterThread
  4843.  
  4844. // --------------------------------------------------------------------------------------------------------------
  4845. #pragma segment Initialize
  4846.  
  4847. static OSErr CreateThread(ThreadEntryProcPtr pThread, void* threadParam, ThreadID* ptid)
  4848. {
  4849.     OSErr    anErr;
  4850.     
  4851.     anErr = NewThread(kCooperativeThread, pThread, threadParam, 0, kNewSuspend,
  4852.         &gThreadResults, ptid);
  4853.         
  4854.     if (anErr == noErr && gStarterThread == kNoThreadID)
  4855.         {
  4856.         anErr = NewThread(kCooperativeThread, StarterThread, NULL, 0, kNewSuspend,
  4857.             &gThreadResults, &gStarterThread);
  4858.         if (anErr != noErr)
  4859.             DisposeThread(*ptid, &gThreadResults, false);
  4860.             // anErr remains != noErr
  4861.         }
  4862.         
  4863.     return anErr;
  4864. }
  4865.         
  4866. // --------------------------------------------------------------------------------------------------------------
  4867. // must be in Main because it runs in a thread and we don't want the segment unloaded
  4868. // while some other thread is running
  4869. #pragma segment Main
  4870.  
  4871. static OSErr FindSimpleTextGuideFile(FSSpec *pSpec)
  4872. {
  4873.     OSErr    anErr = fnfErr;
  4874.     short    numDBs, index;
  4875.     short    folderIndex;
  4876.     
  4877.     for (folderIndex = 0; folderIndex < 3; ++folderIndex)
  4878.         {
  4879.         switch (folderIndex)
  4880.             {
  4881.             case 0:
  4882.                 FindFolder(-1, kPreferencesFolderType, false, &pSpec->vRefNum, &pSpec->parID);
  4883.                 break;
  4884.  
  4885.             case 1:
  4886.                 FindFolder(-1, kExtensionFolderType, false, &pSpec->vRefNum, &pSpec->parID);
  4887.                 break;
  4888.  
  4889.             case 2:
  4890.                 FindFolder(-1, kSystemFolderType, false, &pSpec->vRefNum, &pSpec->parID);
  4891.                 break;
  4892.             }
  4893.         
  4894.         numDBs = AGFileGetDBCount(pSpec->vRefNum, pSpec->parID, kAGFileDBTypeAny, false);
  4895.         
  4896.         if (!gDontYield)
  4897.             YieldToAnyThread();
  4898.         
  4899.         for (index = 0; index < numDBs; ++index)
  4900.             {
  4901.             if (!gDontYield)
  4902.                 YieldToAnyThread();
  4903.             
  4904.             if (AGFileGetIndDB(pSpec->vRefNum, pSpec->parID, kAGFileDBTypeAny, false, index+1, pSpec) == noErr)
  4905.                 {
  4906.                 OSType    creator;
  4907.                 
  4908.                 if ((AGFileGetHelpMenuAppCreator(pSpec, &creator) == noErr) && (creator == 'ttxt'))
  4909.                     return(noErr);
  4910.                 }
  4911.             }
  4912.         }
  4913.     
  4914.     return(anErr);
  4915.     
  4916. } // FindSimpleTextGuideFile
  4917.  
  4918. // --------------------------------------------------------------------------------------------------------------
  4919. // must be in Main because it runs in a thread and we don't want the segment unloaded
  4920. // while some other thread is running
  4921. #pragma segment Main
  4922.  
  4923. static pascal void* AGThread(void *threadParam)
  4924. {
  4925.     #pragma unused(threadParam)
  4926.     
  4927.     if ( !(AGGetAvailableDBTypes() & kAGDBTypeBitAny) )
  4928.         {
  4929.         // if we find one, add it to the help menu and add the right command
  4930.         if (FindSimpleTextGuideFile(&gAGSpec) == noErr)
  4931.             {
  4932.             MenuHandle    helpMenu;
  4933.                         
  4934.             if (HMGetHelpMenuHandle(&helpMenu) == noErr)
  4935.                 {
  4936.                 Str255    tempString;
  4937.                 short    rnSave;
  4938.                 
  4939.                 AGFileGetDBMenuName(&gAGSpec, tempString);
  4940.                 AppendMenu(helpMenu, tempString);
  4941.                 
  4942.                 // since we're in a separate thread, so other resource file may
  4943.                 // be open in front of SimpleText's resource fork
  4944.                 rnSave = CurResFile();
  4945.                 UseResFile(gApplicationResFile);
  4946.                 
  4947.                 GetIndString(tempString, kMiscStrings, iHelpMenuCommand);
  4948.                 
  4949.                 UseResFile(rnSave);
  4950.                 
  4951.                 if (tempString[0] != 0)
  4952.                     SetItemCmd(helpMenu, CountMItems(helpMenu), tempString[1]);
  4953.                 }
  4954.             }
  4955.         }
  4956.     
  4957.     // if we have a database, then install a coach handler
  4958.     if ( (AGGetAvailableDBTypes() & kAGDBTypeBitAny) )
  4959.         {
  4960.         AGInstallCoachHandler(NewCoachReplyProc(SimpleTextCoachHandler), 0, &gAGCoachRefNum);
  4961.         }
  4962.     
  4963.     gAGThread = kNoThreadID;
  4964.     return NULL;
  4965.     
  4966. } // AGThread
  4967.  
  4968. // --------------------------------------------------------------------------------------------------------------
  4969. #pragma segment Initialize
  4970.  
  4971. static void FindAndInstallSimpleTextGuide(void)
  4972. {
  4973.     OSErr    anErr = fnfErr;
  4974.     
  4975.     if (gMachineInfo.haveThreads)
  4976.         anErr = CreateThread(AGThread, NULL, &gAGThread);
  4977.     
  4978.     if (anErr != noErr)
  4979.         AGThread(NULL);
  4980.         
  4981. } // FindAndinstallSimpleTextGuide
  4982.  
  4983. // --------------------------------------------------------------------------------------------------------------
  4984. // must be in Main because it runs in a thread and we don't want the segment unloaded
  4985. // while some other thread is running
  4986. #pragma segment Main
  4987.  
  4988. static long SortAndAddMenu(MenuHandle menu, Str255 newItem)
  4989. {
  4990.     short    numInMenu = CountMItems(menu);
  4991.     short    i;
  4992.     Str255    oldItem;
  4993.     
  4994.     for (i = 1; i <= numInMenu; ++i)
  4995.         {
  4996.         GetMenuItemText(menu, i, oldItem);
  4997.         switch(IUCompString(newItem, oldItem))
  4998.             {
  4999.             // already in?  Return index
  5000.             case 0:
  5001.                 return(i);
  5002.                 break;
  5003.                 
  5004.             // less than, keep scanning
  5005.             case 1:
  5006.                 break;
  5007.                 
  5008.             // greater than, add back one
  5009.             case -1:
  5010.                 InsertMenuItem(menu, "\pTom Dowdy", i-1);
  5011.                 SetMenuItemText(menu, i, newItem);
  5012.                 return(i);
  5013.                 break;
  5014.             }
  5015.         }
  5016.         
  5017.     // fall off the end?  add at the end
  5018.     InsertMenuItem(menu, "\pTom Dowdy", numInMenu);
  5019.     SetMenuItemText(menu, numInMenu+1, newItem);
  5020.             
  5021.     return(numInMenu+1);
  5022.     
  5023. } // SortAndAddMenu
  5024.  
  5025. // --------------------------------------------------------------------------------------------------------------
  5026. // must be in Main because it runs in a thread and we don't want the segment unloaded
  5027. // while some other thread is running
  5028. #pragma segment Main
  5029.  
  5030. static void AddToGlobalList(gxFont fontID, short fond, Style styleBits)
  5031. {
  5032.     if (!gFontMappingList)
  5033.         gFontMappingList = (FontMappingHandle)NewHandle(0);
  5034.         
  5035.     if (gFontMappingList)
  5036.         {
  5037.         Size            oldSize = GetHandleSize((Handle) gFontMappingList);
  5038.         FontMappingPtr    pList;
  5039.         
  5040.         SetHandleSize((Handle)gFontMappingList, oldSize + sizeof(FontMappingRecord));
  5041.         pList = &(*gFontMappingList)[oldSize/sizeof(FontMappingRecord)];
  5042.         pList->fontID = fontID;
  5043.         pList->qdFont = fond;
  5044.         pList->qdStyle = styleBits;
  5045.         }
  5046.         
  5047. } // AddToGlobalList
  5048.  
  5049. // --------------------------------------------------------------------------------------------------------------
  5050. // must be in Main because it runs in a thread and we don't want the segment unloaded
  5051. // while some other thread is running
  5052. #pragma segment Main
  5053.  
  5054. static void AddEachEntry(Handle fond, short* sp, short entries, gxStyle theStyle, MenuHandle menu, short* pStylesUsed)
  5055. {
  5056.     gxFont            fontID;
  5057.     long            length;
  5058.     short             resID;
  5059.     OSType             resType;
  5060.     Str255             resName;
  5061.     Style            styleBits;
  5062.     
  5063.     GetResInfo(fond, &resID, &resType, resName);
  5064.     
  5065.     for (; entries >= 0; --entries)
  5066.         {   
  5067.         if (*sp == 0)
  5068.             {   
  5069.             styleBits = sp[1];
  5070.  
  5071.             // map the font ID and the style bits for this style into GX space
  5072.             (void)GXConvertQDFont(theStyle, resID, styleBits);
  5073.             
  5074.             if (!gDontYield)
  5075.                 YieldToAnyThread();
  5076.     
  5077.             fontID = GXGetStyleFont(theStyle);
  5078.             if (fontID)
  5079.                 length = GXFindFontName(fontID, gxFamilyFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, nil, nil);
  5080.             else
  5081.                 length = 0;
  5082.                 
  5083.             if (!gDontYield)
  5084.                 YieldToAnyThread();
  5085.     
  5086.             if (length)
  5087.                 {
  5088.                 unsigned char     * name;
  5089.                 short            where;
  5090.                 
  5091.                 name = (unsigned char*) NewPtr(length+1);
  5092.                 if (name)
  5093.                     {
  5094.                     MenuHandle    subMenu;
  5095.                     short        mark;
  5096.  
  5097.                     // add this font to the list we know about
  5098.                     AddToGlobalList(fontID, resID, styleBits);
  5099.  
  5100.                     // find the family name for this font
  5101.                     GXFindFontName(fontID, gxFamilyFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, &name[1], nil);
  5102.                     name[0] = length;
  5103.                     
  5104.                     // add the name if new. Then add sub menus
  5105.                     where = SortAndAddMenu(menu, name);
  5106.                     
  5107.                     // do we already have a sub menu?  Or does this font have multiple styles?
  5108.                     GetItemMark(menu, where, &mark);
  5109.                     if ( (mark != 0) || (GXFindFonts(fontID, 0, 0, 0, 0, 0, nil, 1, gxSelectToEnd, nil) > 1) )
  5110.                         {
  5111.                         if (!gDontYield)
  5112.                             YieldToAnyThread();
  5113.     
  5114.                         // make a new menu or grab the old one
  5115.                         if (mark == 0)
  5116.                             subMenu = NewMenu(mFontSubMenusStart + *pStylesUsed, name);
  5117.                         else
  5118.                             subMenu = GetMenuHandle(mark);
  5119.                         DisposePtr((Ptr) name);
  5120.                         
  5121.                         length = GXFindFontName(fontID, gxStyleFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, nil, nil);
  5122.                         if (length)
  5123.                             {
  5124.                             name = (unsigned char*) NewPtr(length+1);
  5125.                             if (name)
  5126.                                 {
  5127.                                 GXFindFontName(fontID, gxStyleFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, &name[1], nil);
  5128.                                 name[0] = length;
  5129.                                 (void) SortAndAddMenu(subMenu, name);
  5130.                                 DisposePtr((Ptr) name);
  5131.                                 }
  5132.                             }
  5133.                         
  5134.                         // if new menu, add to the master
  5135.                         if (mark == 0)
  5136.                             {
  5137.                             InsertMenu(subMenu, -1);
  5138.                             SetItemCmd(menu, where, hMenuCmd);
  5139.                             SetItemMark(menu, where, mFontSubMenusStart + *pStylesUsed);
  5140.                             (*pStylesUsed)++;
  5141.                             }
  5142.                         }
  5143.                     else
  5144.                         DisposePtr((Ptr) name);
  5145.  
  5146.                     }
  5147.                 }
  5148.                 
  5149.             } // if (*sp == 0)
  5150.             
  5151.         sp += 3;        /* three elements in the FAT */
  5152.         
  5153.         if (!gDontYield)
  5154.             YieldToAnyThread();
  5155.  
  5156.         } // for (# entries)
  5157. } // AddEachEntry
  5158.  
  5159. // --------------------------------------------------------------------------------------------------------------
  5160. // must be in Main because it runs in a thread and we don't want the segment unloaded
  5161. // while some other thread is running
  5162. #pragma segment Main
  5163.  
  5164. static pascal void* FontsThread(void *threadParam)
  5165. {
  5166.     MenuHandle    menu = (MenuHandle) threadParam;
  5167.     long        numberFonts;
  5168.     long        i;
  5169.     short        stylesUsed = 0;
  5170.     gxStyle        theStyle;
  5171.     Boolean        menusAdjusted = false;
  5172.     
  5173.     theStyle = GXNewStyle();
  5174.     
  5175.     numberFonts = CountResources('FOND');
  5176.     for (i = 1; i <= numberFonts; ++i)
  5177.         {
  5178.         Handle     fond = GetIndResource('FOND', i);
  5179.         
  5180.         if (!ResError() && fond && *fond)
  5181.             do
  5182.             {   
  5183.             short* sp = (short*)(*fond + sizeof(FamRec));
  5184.             short entries = *sp++;
  5185.             
  5186.             AddEachEntry(fond, sp, entries, theStyle, menu, &stylesUsed);
  5187.             
  5188.             // now that there are some fonts in the font menu, make sure the menu's enabled
  5189.             if (!menusAdjusted && CountMItems(menu) > 0)
  5190.                 {
  5191.                 AdjustMenus(FrontWindow(), true, false);
  5192.                 menusAdjusted = true;
  5193.                 }
  5194.                 
  5195.             } while ((fond = GetNextFOND(fond)) != 0);
  5196.             
  5197.         } // for (# fonts)
  5198.  
  5199.     GXDisposeStyle(theStyle);
  5200.     
  5201.     gFontThread = kNoThreadID;
  5202.     return 0;
  5203. } // FontsThread
  5204.  
  5205. // --------------------------------------------------------------------------------------------------------------
  5206. #pragma segment Initialize
  5207.  
  5208. static OSErr BuildFontMenu(MenuHandle menu)
  5209. {
  5210.     OSErr    anErr = noErr;
  5211.     
  5212.     if (gMachineInfo.haveGX)
  5213.         {
  5214.         (void) DoStartupGX();
  5215.         
  5216.         // prime the font cache so we don't spend time doing this in the font thread
  5217.         GXGetDefaultFont();
  5218.         
  5219.         anErr = paramErr;
  5220.         if (gMachineInfo.haveThreads)
  5221.             anErr = CreateThread(FontsThread, menu, &gFontThread);
  5222.  
  5223.         if (anErr != noErr)
  5224.             {
  5225.             FontsThread(menu);
  5226.             anErr = noErr;
  5227.             }
  5228.         }
  5229.     else
  5230.         AppendResMenu(menu, 'FONT');
  5231.     
  5232.     return(anErr);
  5233.     
  5234. } // BuildFontMenu
  5235.  
  5236. // --------------------------------------------------------------------------------------------------------------
  5237. #pragma segment Initialize
  5238.  
  5239. static OSErr    DoInitialize(void)
  5240. {
  5241.     short                count;            // loop counter
  5242.     Handle                menuBar;        // for loading our menus in
  5243.     gxGraphicsError        anErr = noErr;    // any errors we get, none so far
  5244.     long                version;        // version for Gestalt calls
  5245.     
  5246.     InitGraf((Ptr) &qd.thePort);
  5247.     InitFonts();
  5248.     InitWindows();
  5249.     InitMenus();
  5250.     TEInit();
  5251.     InitDialogs(nil);
  5252.     InitCursor();
  5253.     
  5254.     gAllDone = false;
  5255.     
  5256.     // check that the system is correct to handle things
  5257.     SysEnvirons(1, &gMachineInfo.theEnvirons);
  5258.     if (gMachineInfo.theEnvirons.systemVersion < 0x0700)
  5259.         {
  5260.         // Wait for app to come to front
  5261.         for (count = 1; count <= 3; ++count)
  5262.             EventAvail(everyEvent, &gEvent);
  5263.             
  5264.         anErr = eMachineToOld;
  5265.         nrequire(anErr, SysEnvirons);
  5266.         }
  5267.  
  5268.     gMachineInfo.lastBalloonIndex = iNoBalloon;
  5269.     gMachineInfo.amInBackground = false;
  5270.     gMachineInfo.documentCount  = 1;
  5271.     gMachineInfo.haveQuickTime     = (Gestalt(gestaltQuickTime, &version) == noErr);
  5272.     gMachineInfo.haveRecording     = (Gestalt(gestaltSoundAttr, &version) == noErr) && ((version & (1<<gestaltHasSoundInputDevice)) != 0);
  5273.     gMachineInfo.haveTTS         = (Gestalt(gestaltSpeechAttr, &version) == noErr) && ((version & (1<<gestaltSpeechMgrPresent)) != 0);
  5274.     gMachineInfo.haveGX            = (Gestalt(gestaltGXVersion, &version) == noErr);
  5275.     gMachineInfo.haveTSM         = (Gestalt(gestaltTSMgrVersion, &version) == noErr) && (version >= 1);
  5276.     gMachineInfo.haveTSMTE         = (Gestalt(gestaltTSMTEAttr, &version) == noErr) && ((version & (1<<gestaltTSMTE)) != 0);
  5277.     gMachineInfo.haveDragMgr    = (Gestalt(gestaltDragMgrAttr, &version) == noErr) && ((version & (1<<gestaltDragMgrPresent)) != 0) &&
  5278.                                     (Gestalt(gestaltTEAttr, &version) == noErr) && ((version & (1<<gestaltTEHasGetHiliteRgn)) != 0);
  5279.     gMachineInfo.haveThreeD        = false;
  5280.     gMachineInfo.haveAppleGuide    = (Gestalt(gestaltHelpMgrAttr, &version) == noErr) && ((version & (1<<gestaltAppleGuidePresent)) != 0);
  5281.     gMachineInfo.haveThreads    = (Gestalt(gestaltThreadMgrAttr, &version) == noErr) && ((version & (1<<gestaltThreadMgrPresent)) != 0);
  5282.     
  5283.     #if GENERATINGPOWERPC
  5284.         {
  5285.         CFragConnectionID    connID;
  5286.         Ptr                 mainAddr;
  5287.         Str255                errName;
  5288.         
  5289.         if ( (gMachineInfo.haveQuickTime)     && (GetSharedLibrary("\pQuickTimeLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5290.             gMachineInfo.haveQuickTime = false;
  5291.         if ( (gMachineInfo.haveTTS)         && (GetSharedLibrary("\pSpeechLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5292.             gMachineInfo.haveTTS = false;
  5293.         if ( (gMachineInfo.haveGX)             && (GetSharedLibrary("\pQuickDrawGXLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5294.             gMachineInfo.haveGX = false;
  5295.         if ( (gMachineInfo.haveDragMgr)        && (GetSharedLibrary("\pDragLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5296.             gMachineInfo.haveDragMgr = false;
  5297.         if ( (gMachineInfo.haveThreads)     && (GetSharedLibrary("\pThreadsLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5298.             gMachineInfo.haveThreads = false;
  5299.         }
  5300.     #endif
  5301.     
  5302.  
  5303.     // initialize text services if they exist
  5304.     if (gMachineInfo.haveTSMTE)
  5305.         {
  5306.         if (InitTSMAwareApplication() != noErr)
  5307.             {
  5308.             gMachineInfo.haveTSM = false;
  5309.             gMachineInfo.haveTSMTE = false;
  5310.             }
  5311.         }
  5312.         
  5313.     // save away info we need from the get-go    
  5314.     gApplicationResFile = CurResFile();
  5315.     gCursorRgn = NewRgn();
  5316.  
  5317.     // load up the menus
  5318.     menuBar = (Handle) GetNewMBar(rMenuBar);            /* read menus into menu bar */
  5319.     anErr = ResError();
  5320.     if ( (anErr == noErr) && (menuBar == nil) )
  5321.         anErr = resNotFound;
  5322.     nrequire(anErr, GetNewMBar);
  5323.     
  5324.     // install menus
  5325.     SetMenuBar(menuBar);    
  5326.     DisposeHandle(menuBar);
  5327.  
  5328.     // build the Apple menu
  5329.     AppendResMenu(GetMenuHandle(mApple), 'DRVR');    /* add DA names to Apple menu */
  5330.     
  5331.     // haven't yet done a startup of QuickDraw GX
  5332.     if (gMachineInfo.haveGX)
  5333.         gMachineInfo.haveStartedGX = false;
  5334.  
  5335.     // Build the font menu
  5336.     anErr = BuildFontMenu(GetMenuHandle(mFont));
  5337.     nrequire(anErr, BuildFontMenu);
  5338.     
  5339.     // insert our heirarchical menus
  5340.     {
  5341.     MenuHandle     menu = GetMenu( mVoices );
  5342.     short        menuID, itemID;
  5343.     
  5344.     InsertMenu( menu, hierMenu );
  5345.     
  5346.     CommandToIDs(cSelectVoice, &menuID, &itemID);
  5347.     menu = GetMenuHandle(menuID);
  5348.  
  5349.     SetItemCmd( menu, itemID, hMenuCmd );
  5350.     SetItemMark( menu, itemID, mVoices );
  5351.     }
  5352.  
  5353.     AdjustMenus(nil, true, false);
  5354.     DrawMenuBar();
  5355.     
  5356.     // start up QuickTime, but problems result in us pretending not to have it
  5357.     if (gMachineInfo.haveQuickTime)
  5358.         if (EnterMovies() != noErr)
  5359.             gMachineInfo.haveQuickTime = false;
  5360.         
  5361.     // Install AppleEvent handlers for the base classes
  5362.  
  5363.     #define INSTALL(event, handler) \
  5364.             AEInstallEventHandler(kCoreEventClass, event, handler, 0, false)
  5365.  
  5366.     INSTALL (kAEOpenApplication, gDoOpenApp);
  5367.     INSTALL (kAEQuitApplication, gDoQuitApp);
  5368.     INSTALL (kAEOpenDocuments,   gDoOpenDocument);
  5369.     INSTALL (kAEPrintDocuments,  gDoPrintDocument);
  5370.  
  5371.     #undef INSTALL
  5372.  
  5373.     // Install our global dragging procs, but only if we have Drag and Drop. An error results
  5374.     // in us pretending that we don't have drag support. Notice that in the test above, we also
  5375.     // require TextEdit to have TEGetHiliteRgn avalilable, which is always the case with the
  5376.     // present Drag Manager.
  5377.  
  5378.     if (gMachineInfo.haveDragMgr)
  5379.         {
  5380.         gGlobalTrackingHandler = NewDragTrackingHandlerProc(GlobalTrackingHandler);
  5381.         gGlobalReceiveHandler = NewDragReceiveHandlerProc(GlobalReceiveHandler);
  5382.         
  5383.         anErr = InstallTrackingHandler(gGlobalTrackingHandler, nil, nil);
  5384.  
  5385.         if (anErr == noErr)
  5386.             {
  5387.             anErr = InstallReceiveHandler(gGlobalReceiveHandler, nil, nil);
  5388.  
  5389.             if (anErr != noErr)
  5390.                 {
  5391.                 RemoveTrackingHandler(gGlobalTrackingHandler, nil);
  5392.                 gMachineInfo.haveDragMgr = false;
  5393.                 }
  5394.             }
  5395.         else
  5396.             gMachineInfo.haveDragMgr = false;
  5397.         }
  5398.  
  5399.  
  5400.     // verify that the AppleGuide database is available, and if it isn't, try to find
  5401.     // it from other places -- but don't bother on 2.1, because they made changes
  5402.     // to break our location finding (sigh!)
  5403.     if  (
  5404.         (gMachineInfo.haveAppleGuide) && 
  5405.         ( (Gestalt('ag_v', &version) != noErr) || (version < 0x00000210) )
  5406.         )
  5407.         FindAndInstallSimpleTextGuide();
  5408.         
  5409.  
  5410.     return noErr;
  5411.     
  5412.     
  5413. // EXCEPTION HANDLING
  5414. BuildFontMenu:
  5415. GetNewMBar:
  5416. SysEnvirons:
  5417.     ConductErrorDialog(anErr, cNull, cancel);
  5418.     
  5419.     return anErr;
  5420.  
  5421. } // DoInitialize
  5422.  
  5423. // --------------------------------------------------------------------------------------------------------------
  5424. #pragma segment Terminate
  5425.  
  5426. static OSErr    DoTerminate(void)
  5427. {
  5428.     OSErr    anErr = noErr;
  5429.     
  5430.     if (gFontThread != kNoThreadID)
  5431.         DisposeThread(gFontThread, &gThreadResults, false);
  5432.     if (gAGThread != kNoThreadID)
  5433.         DisposeThread(gAGThread, &gThreadResults, false);
  5434.     if (gStarterThread != kNoThreadID)
  5435.         DisposeThread(gStarterThread, &gThreadResults, false);
  5436.  
  5437.     if (gMachineInfo.haveQuickTime)
  5438.         ExitMovies();
  5439.  
  5440.     if ( (gMachineInfo.haveGX) && (GXGetGraphicsClient() != nil) )
  5441.         {
  5442.         GXExitPrinting();
  5443.         GXExitGraphics();
  5444.         }
  5445.         
  5446.     if (gMachineInfo.haveTSMTE)
  5447.         CloseTSMAwareApplication();
  5448.  
  5449.     if (gMachineInfo.haveDragMgr)
  5450.         {
  5451.         RemoveReceiveHandler(gGlobalReceiveHandler, nil);
  5452.         RemoveTrackingHandler(gGlobalTrackingHandler, nil);
  5453.         }
  5454.         
  5455.     if (gMachineInfo.haveAppleGuide)
  5456.         {
  5457.         if ((gAGRefNum != -1) && AGIsDatabaseOpen(gAGRefNum))
  5458.             {
  5459.             AGClose(&gAGRefNum);
  5460.             gAGRefNum = -1;
  5461.             }
  5462.         if (gAGCoachRefNum != -1)
  5463.             AGRemoveCoachHandler(&gAGCoachRefNum);
  5464.         }
  5465.  
  5466.     return anErr;
  5467.     
  5468. } // DoTerminate
  5469.  
  5470. // --------------------------------------------------------------------------------------------------------------
  5471. #pragma segment Main
  5472.  
  5473. main(void)
  5474. {
  5475.     OSErr    anErr;
  5476.     
  5477. #ifndef __MWERKS__
  5478.     UnloadSeg((Ptr) _DataInit);                        /* note that _DataInit must not be in Main! */
  5479. #endif
  5480.     MaxApplZone();                                    /* expand the heap so code segments load at the top */
  5481.     MoreMasters(); MoreMasters(); MoreMasters();     /* we love handles */
  5482.     anErr = DoInitialize();
  5483.     UnloadSeg((Ptr) DoInitialize);                    
  5484.     if (anErr == noErr)
  5485.         {
  5486.         DoEventLoop();
  5487.  
  5488. // REVIEW: don't want to unload the segment we're in!!
  5489. //        UnloadSeg((Ptr) DoEventLoop);
  5490.         DoTerminate();                    
  5491.         }
  5492.     return 0;
  5493. } // main
  5494.